vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / debug / debug_paranoia.cpp
blob3a1d2da09ac517830010f1e23f904fdf2f0dc58a
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <debug_paranoia.h>
8 #include <sys/param.h>
10 #include <new>
12 #include <OS.h>
14 #include <tracing.h>
15 #include <util/AutoLock.h>
18 #if ENABLE_PARANOIA_CHECKS
21 // #pragma mark - CRC-32
24 static const uint32 kCRC32Polynom = 0x04c11db7;
25 static uint32 sCRC32Table[256];
28 static uint32
29 crc32_reflect(uint32 value, int32 bits)
31 uint32 result = 0;
32 for (int32 i = 1; i <= bits; i++) {
33 if (value & 1)
34 result |= 1 << (bits - i);
35 value >>= 1;
38 return result;
42 static void
43 init_crc32_table()
45 for (int32 i = 0; i < 256; i++) {
46 sCRC32Table[i] = crc32_reflect(i, 8) << 24;
47 for (int32 k = 0; k < 8; k++) {
48 sCRC32Table[i] = (sCRC32Table[i] << 1)
49 ^ (sCRC32Table[i] & (1 << 31) ? kCRC32Polynom : 0);
51 sCRC32Table[i] = crc32_reflect(sCRC32Table[i], 32);
56 static uint32
57 crc32(const void* _data, size_t size)
59 uint8* data = (uint8*)_data;
60 uint32 crc = 0xffffffff;
62 while (size-- > 0) {
63 crc = (crc >> 8) ^ sCRC32Table[(crc & 0xff) ^ *data];
64 data++;
67 return crc;
71 // #pragma mark - ParanoiaCheck[Set]
74 class ParanoiaCheckSet;
76 class ParanoiaCheck {
77 public:
78 ParanoiaCheck(const void* address, size_t size)
80 fAddress(address),
81 fSize(size)
83 Update();
86 const void* Address() const { return fAddress; }
87 size_t Size() const { return fSize; }
89 void Update()
91 fCheckSum = crc32(fAddress, fSize);
94 bool Check() const
96 return crc32(fAddress, fSize) == fCheckSum;
99 private:
100 const void* fAddress;
101 size_t fSize;
102 uint32 fCheckSum;
103 ParanoiaCheck* fNext;
105 friend class ParanoiaCheckSet;
109 class ParanoiaCheckSet {
110 public:
111 ParanoiaCheckSet(const void* object, const char* description)
113 fObject(object),
114 fDescription(description),
115 fChecks(NULL)
119 const void* Object() const { return fObject; }
120 const char* Description() const { return fDescription; }
122 ParanoiaCheck* FirstCheck() const
124 return fChecks;
127 ParanoiaCheck* NextCheck(ParanoiaCheck* check) const
129 return check->fNext;
132 ParanoiaCheck* FindCheck(const void* address) const
134 ParanoiaCheck* check = fChecks;
135 while (check != NULL && check->Address() != address)
136 check = check->fNext;
137 return check;
140 void AddCheck(ParanoiaCheck* check)
142 check->fNext = fChecks;
143 fChecks = check;
146 void RemoveCheck(ParanoiaCheck* check)
148 if (check == fChecks) {
149 fChecks = check->fNext;
150 return;
153 ParanoiaCheck* previous = fChecks;
154 while (previous != NULL && previous->fNext != check)
155 previous = previous->fNext;
157 // if previous is NULL (which it shouldn't be), just crash here
158 previous->fNext = check->fNext;
161 ParanoiaCheck* RemoveFirstCheck()
163 ParanoiaCheck* check = fChecks;
164 if (check == NULL)
165 return NULL;
167 fChecks = check->fNext;
168 return check;
171 void SetHashNext(ParanoiaCheckSet* next)
173 fHashNext = next;
176 ParanoiaCheckSet* HashNext() const
178 return fHashNext;
181 private:
182 const void* fObject;
183 const char* fDescription;
184 ParanoiaCheck* fChecks;
185 ParanoiaCheckSet* fHashNext;
189 union paranoia_slot {
190 uint8 check[sizeof(ParanoiaCheck)];
191 uint8 checkSet[sizeof(ParanoiaCheckSet)];
192 paranoia_slot* nextFree;
196 // #pragma mark - Tracing
199 #if PARANOIA_TRACING
202 namespace ParanoiaTracing {
204 class ParanoiaTraceEntry : public AbstractTraceEntry {
205 public:
206 ParanoiaTraceEntry(const void* object)
208 fObject(object)
210 #if PARANOIA_TRACING_STACK_TRACE
211 fStackTrace = capture_tracing_stack_trace(PARANOIA_TRACING_STACK_TRACE,
212 1, false);
213 #endif
216 #if PARANOIA_TRACING_STACK_TRACE
217 virtual void DumpStackTrace(TraceOutput& out)
219 out.PrintStackTrace(fStackTrace);
221 #endif
223 protected:
224 const void* fObject;
225 #if PARANOIA_TRACING_STACK_TRACE
226 tracing_stack_trace* fStackTrace;
227 #endif
231 class CreateCheckSet : public ParanoiaTraceEntry {
232 public:
233 CreateCheckSet(const void* object, const char* description)
235 ParanoiaTraceEntry(object)
237 fDescription = alloc_tracing_buffer_strcpy(description, 64, false);
238 Initialized();
241 virtual void AddDump(TraceOutput& out)
243 out.Print("paranoia create check set: object: %p, "
244 "description: \"%s\"", fObject, fDescription);
247 private:
248 const char* fDescription;
252 class DeleteCheckSet : public ParanoiaTraceEntry {
253 public:
254 DeleteCheckSet(const void* object)
256 ParanoiaTraceEntry(object)
258 Initialized();
261 virtual void AddDump(TraceOutput& out)
263 out.Print("paranoia delete check set: object: %p", fObject);
268 class SetCheck : public ParanoiaTraceEntry {
269 public:
270 SetCheck(const void* object, const void* address, size_t size,
271 paranoia_set_check_mode mode)
273 ParanoiaTraceEntry(object),
274 fAddress(address),
275 fSize(size),
276 fMode(mode)
278 Initialized();
281 virtual void AddDump(TraceOutput& out)
283 const char* mode = "??? op:";
284 switch (fMode) {
285 case PARANOIA_DONT_FAIL:
286 mode = "set: ";
287 break;
288 case PARANOIA_FAIL_IF_EXISTS:
289 mode = "add: ";
290 break;
291 case PARANOIA_FAIL_IF_MISSING:
292 mode = "update:";
293 break;
295 out.Print("paranoia check %s object: %p, address: %p, size: %lu",
296 mode, fObject, fAddress, fSize);
299 private:
300 const void* fAddress;
301 size_t fSize;
302 paranoia_set_check_mode fMode;
306 class RemoveCheck : public ParanoiaTraceEntry {
307 public:
308 RemoveCheck(const void* object, const void* address, size_t size)
310 ParanoiaTraceEntry(object),
311 fAddress(address),
312 fSize(size)
314 Initialized();
317 virtual void AddDump(TraceOutput& out)
319 out.Print("paranoia check remove: object: %p, address: %p, size: "
320 "%lu", fObject, fAddress, fSize);
323 private:
324 const void* fAddress;
325 size_t fSize;
326 paranoia_set_check_mode fMode;
330 } // namespace ParanoiaTracing
332 # define T(x) new(std::nothrow) ParanoiaTracing::x
334 #else
335 # define T(x)
336 #endif // PARANOIA_TRACING
339 // #pragma mark -
342 #define PARANOIA_HASH_SIZE PARANOIA_SLOT_COUNT
344 static paranoia_slot sSlots[PARANOIA_SLOT_COUNT];
345 static paranoia_slot* sSlotFreeList;
346 static ParanoiaCheckSet* sCheckSetHash[PARANOIA_HASH_SIZE];
347 static spinlock sParanoiaLock;
350 static paranoia_slot*
351 allocate_slot()
353 if (sSlotFreeList == NULL)
354 return NULL;
356 paranoia_slot* slot = sSlotFreeList;
357 sSlotFreeList = slot->nextFree;
358 return slot;
362 static void
363 free_slot(paranoia_slot* slot)
365 slot->nextFree = sSlotFreeList;
366 sSlotFreeList = slot;
370 static void
371 add_check_set(ParanoiaCheckSet* set)
373 int slot = (addr_t)set->Object() % PARANOIA_HASH_SIZE;
374 set->SetHashNext(sCheckSetHash[slot]);
375 sCheckSetHash[slot] = set;
379 static void
380 remove_check_set(ParanoiaCheckSet* set)
382 int slot = (addr_t)set->Object() % PARANOIA_HASH_SIZE;
383 if (set == sCheckSetHash[slot]) {
384 sCheckSetHash[slot] = set->HashNext();
385 return;
388 ParanoiaCheckSet* previousSet = sCheckSetHash[slot];
389 while (previousSet != NULL && previousSet->HashNext() != set)
390 previousSet = previousSet->HashNext();
392 // if previousSet is NULL (which it shouldn't be), just crash here
393 previousSet->SetHashNext(set->HashNext());
397 static ParanoiaCheckSet*
398 lookup_check_set(const void* object)
400 int slot = (addr_t)object % PARANOIA_HASH_SIZE;
401 ParanoiaCheckSet* set = sCheckSetHash[slot];
402 while (set != NULL && set->Object() != object)
403 set = set->HashNext();
405 return set;
408 // #pragma mark - public interface
411 status_t
412 create_paranoia_check_set(const void* object, const char* description)
414 T(CreateCheckSet(object, description));
416 if (object == NULL) {
417 panic("create_paranoia_check_set(): NULL object");
418 return B_BAD_VALUE;
421 InterruptsSpinLocker _(sParanoiaLock);
423 // check, if object is already registered
424 ParanoiaCheckSet* set = lookup_check_set(object);
425 if (set != NULL) {
426 panic("create_paranoia_check_set(): object %p already has a check set",
427 object);
428 return B_BAD_VALUE;
431 // allocate slot
432 paranoia_slot* slot = allocate_slot();
433 if (slot == NULL) {
434 panic("create_paranoia_check_set(): out of free slots");
435 return B_NO_MEMORY;
438 set = new(slot) ParanoiaCheckSet(object, description);
439 add_check_set(set);
441 return B_OK;
445 status_t
446 delete_paranoia_check_set(const void* object)
448 T(DeleteCheckSet(object));
450 InterruptsSpinLocker _(sParanoiaLock);
452 // get check set
453 ParanoiaCheckSet* set = lookup_check_set(object);
454 if (set == NULL) {
455 panic("delete_paranoia_check_set(): object %p doesn't have a check set",
456 object);
457 return B_BAD_VALUE;
460 // free all checks
461 while (ParanoiaCheck* check = set->RemoveFirstCheck())
462 free_slot((paranoia_slot*)check);
464 // free check set
465 remove_check_set(set);
466 free_slot((paranoia_slot*)set);
468 return B_OK;
472 status_t
473 run_paranoia_checks(const void* object)
475 InterruptsSpinLocker _(sParanoiaLock);
477 // get check set
478 ParanoiaCheckSet* set = lookup_check_set(object);
479 if (set == NULL) {
480 panic("run_paranoia_checks(): object %p doesn't have a check set",
481 object);
482 return B_BAD_VALUE;
485 status_t error = B_OK;
487 ParanoiaCheck* check = set->FirstCheck();
488 while (check != NULL) {
489 if (!check->Check()) {
490 panic("paranoia check failed for object %p (%s), address: %p, "
491 "size: %lu", set->Object(), set->Description(),
492 check->Address(), check->Size());
493 error = B_BAD_DATA;
496 check = set->NextCheck(check);
499 return error;
503 status_t
504 set_paranoia_check(const void* object, const void* address, size_t size,
505 paranoia_set_check_mode mode)
507 T(SetCheck(object, address, size, mode));
509 InterruptsSpinLocker _(sParanoiaLock);
511 // get check set
512 ParanoiaCheckSet* set = lookup_check_set(object);
513 if (set == NULL) {
514 panic("set_paranoia_check(): object %p doesn't have a check set",
515 object);
516 return B_BAD_VALUE;
519 // update check, if already existing
520 ParanoiaCheck* check = set->FindCheck(address);
521 if (check != NULL) {
522 if (mode == PARANOIA_FAIL_IF_EXISTS) {
523 panic("set_paranoia_check(): object %p already has a check for "
524 "address %p", object, address);
525 return B_BAD_VALUE;
528 if (check->Size() != size) {
529 panic("set_paranoia_check(): changing check sizes not supported");
530 return B_BAD_VALUE;
533 check->Update();
534 return B_OK;
537 if (mode == PARANOIA_FAIL_IF_MISSING) {
538 panic("set_paranoia_check(): object %p doesn't have a check for "
539 "address %p yet", object, address);
540 return B_BAD_VALUE;
543 // allocate slot
544 paranoia_slot* slot = allocate_slot();
545 if (slot == NULL) {
546 panic("set_paranoia_check(): out of free slots");
547 return B_NO_MEMORY;
550 check = new(slot) ParanoiaCheck(address, size);
551 set->AddCheck(check);
553 return B_OK;
557 status_t
558 remove_paranoia_check(const void* object, const void* address, size_t size)
560 T(RemoveCheck(object, address, size));
562 InterruptsSpinLocker _(sParanoiaLock);
564 // get check set
565 ParanoiaCheckSet* set = lookup_check_set(object);
566 if (set == NULL) {
567 panic("remove_paranoia_check(): object %p doesn't have a check set",
568 object);
569 return B_BAD_VALUE;
572 // get check
573 ParanoiaCheck* check = set->FindCheck(address);
574 if (check == NULL) {
575 panic("remove_paranoia_check(): no check for address %p "
576 "(object %p (%s))", address, object, set->Description());
577 return B_BAD_VALUE;
580 if (check->Size() != size) {
581 panic("remove_paranoia_check(): changing check sizes not "
582 "supported");
583 return B_BAD_VALUE;
586 set->RemoveCheck(check);
587 return B_OK;
591 #endif // ENABLE_PARANOIA_CHECKS
594 void
595 debug_paranoia_init()
597 #if ENABLE_PARANOIA_CHECKS
598 // init CRC-32 table
599 init_crc32_table();
601 // init paranoia slot free list
602 for (int32 i = 0; i < PARANOIA_SLOT_COUNT; i++)
603 free_slot(&sSlots[i]);
604 #endif