Using Assertion in the Linux Kernel

Using assertion in the Linux kernel

The corresponding kernel macros are BUG_ON and WARN_ON. The former is for when you want to make the kernel panic and bring the system down (i.e., unrecoverable error). The latter is for when you want to log something to the kernel log (viewable via dmesg).

As @Michael says, in the kernel, you need to validate anything that comes from userspace and just handle it, whatever it is. BUG_ON and WARN_ON are to catch bugs in your own code or problems with the hardware.

What is 'assert' in a kernel module?

Use KASSERT. It's slightly different from userspace assert(); use it like this:

KASSERT(x == y, ("%s: %d != %d", func, x, y));

KASSERTs are enabled if INVARIANTS option is defined in the kernel config. In 11-CURRENT it's enabled by default; in stable releases it's disabled.

A assertion succeed in userspace but failed in kernel

assert fails when the condition is false

BUG_ON fails when the condition is true, see https://kernelnewbies.org/FAQ/BUG

Your condition is true.

Appropriateness of using assertion

assert(false) is fine. It is a machine readable version of the comment

// should never be reached

I always think of asserts as machine readable (and enforceable) comments

assert(stuff4()) is different. It looks like it is testing the return value of stuff4. This is bad since in non debug builds stuff4 wont be called (for most assert implementations); typically the assert macro compiles to nothing in non debug builds

Ways to ASSERT expressions at build time in C

NEW ANSWER :

In my original answer (below), I had to have two different macros to support assertions in a function scope and at the global scope. I wondered if it was possible to come up with a single solution that would work in both scopes.

I was able to find a solution that worked for Visual Studio and Comeau compilers using extern character arrays. But I was able to find a more complex solution that works for GCC. But GCC's solution doesn't work for Visual Studio. :( But adding a '#ifdef __ GNUC __', it's easy to choose the right set of macros for a given compiler.

Solution:

#ifdef __GNUC__
#define STATIC_ASSERT_HELPER(expr, msg) \
(!!sizeof \ (struct { unsigned int STATIC_ASSERTION__##msg: (expr) ? 1 : -1; }))
#define STATIC_ASSERT(expr, msg) \
extern int (*assert_function__(void)) [STATIC_ASSERT_HELPER(expr, msg)]
#else
#define STATIC_ASSERT(expr, msg) \
extern char STATIC_ASSERTION__##msg[1]; \
extern char STATIC_ASSERTION__##msg[(expr)?1:2]
#endif /* #ifdef __GNUC__ */

Here are the error messages reported for STATIC_ASSERT(1==1, test_message); at line 22 of test.c:

GCC:

line 22: error: negative width in bit-field `STATIC_ASSERTION__test_message'

Visual Studio:

test.c(22) : error C2369: 'STATIC_ASSERTION__test_message' : redefinition; different subscripts
test.c(22) : see declaration of 'STATIC_ASSERTION__test_message'

Comeau:

line 22: error: declaration is incompatible with
"char STATIC_ASSERTION__test_message[1]" (declared at line 22)

 
 

ORIGINAL ANSWER :

I do something very similar to what Checkers does. But I include a message that'll show up in many compilers:

#define STATIC_ASSERT(expr, msg)               \
{ \
char STATIC_ASSERTION__##msg[(expr)?1:-1]; \
(void)STATIC_ASSERTION__##msg[0]; \
}

And for doing something at the global scope (outside a function) use this:

#define GLOBAL_STATIC_ASSERT(expr, msg)   \
extern char STATIC_ASSERTION__##msg[1]; \
extern char STATIC_ASSERTION__##msg[(expr)?1:2]

CUDA: How to assert in kernel code?

For devices of cc 2.x or above, assertion , void assert(int expression), could be used within a kernel such that threads with expression == 0 send a message to stderr once a host synchronization function is called.

For other cases or when assertion cannot be used (e.g. on MacOS), you won't be able to return an error message or error code to the host from a kernel.

Instead, I would set a error state and check it from the host. Use device global memory or (better) mapped host memory for storing an error state, passed as a parameter to each kernel call. Use if statements in the kernel, and of if the statements fail, set the error code and return. You will be able to check the error code from the host after the kernel call, but keep in mind that you will have synchronize the host and device after the kernel launch before checking the error code. I guess this will work fine for development but not so much for production.

As to printing an error message straight from the device

  • In 1.x, 2.x, and 3.0 cards, you can use emulation mode to print an error message.
  • In 3.1 forward (on fermi), apparently you can use printf in kernels to print the error message. It appears that it doesn't always work right away, e.g. http://forums.nvidia.com/index.php?showtopic=182448

Assertion.h in FreeBSD

You can't use /usr/src/lib/libc/include/isc in the kernel. This is part of the user space libc, same goes for assert.h.

You can use the sys/queue.h file if you need linked lists, see e.g. here

For assertions in the kernel, use the KASSERT() macro.

What is :-!! in C code?

This is, in effect, a way to check whether the expression e can be evaluated to be 0, and if not, to fail the build.

The macro is somewhat misnamed; it should be something more like BUILD_BUG_OR_ZERO, rather than ...ON_ZERO. (There have been occasional discussions about whether this is a confusing name.)

You should read the expression like this:

sizeof(struct { int: -!!(e); }))
  1. (e): Compute expression e.

  2. !!(e): Logically negate twice: 0 if e == 0; otherwise 1.

  3. -!!(e): Numerically negate the expression from step 2: 0 if it was 0; otherwise -1.

  4. struct{int: -!!(0);} --> struct{int: 0;}: If it was zero, then we declare a struct with an anonymous integer bitfield that has width zero. Everything is fine and we proceed as normal.

  5. struct{int: -!!(1);} --> struct{int: -1;}: On the other hand, if it isn't zero, then it will be some negative number. Declaring any bitfield with negative width is a compilation error.

So we'll either wind up with a bitfield that has width 0 in a struct, which is fine, or a bitfield with negative width, which is a compilation error. Then we take sizeof that field, so we get a size_t with the appropriate width (which will be zero in the case where e is zero).


Some people have asked: Why not just use an assert?

keithmo's answer here has a good response:

These macros implement a compile-time test, while assert() is a run-time test.

Exactly right. You don't want to detect problems in your kernel at runtime that could have been caught earlier! It's a critical piece of the operating system. To whatever extent problems can be detected at compile time, so much the better.



Related Topics



Leave a reply



Submit