gcc attribute: nonnull

Requires :

  • compiler: gcc 3.3 later

nonnull attribute를 사용하면, function argument에 NULL을 넘기면 안되는 함수에 NULL을 사용하는 경우를 compile-time에 검출할 수 있습니다.

그러나 NULL이 implicitly(묵시적) 으로 지정된 경우에만 감지되고, 그렇지 않은 상황을 검출할 수 없는 한계가 있습니다.
이러한 한계는 이후에 다시 자세히 설명하겠습니다.

이 attribute는 -Wnonnull-Werror=nonnull compile options과 함께 사용되어야 의미가 있습니다.
-Wno-nonnull를 사용하면 애써 nonnull attribute를 사용한 의미가 없게 됩니다.

gcc-3.3 release note에서 nonnull이 처음 소개되었고, kernel이나 glibc와 같이 많은 오픈소스에서 사용되고 있습니다.


GCC 3.3 Changes
C/ObjC/C++
...
A new function attribute, nonnull, has been added which allows pointer arguments to functions to be specified as requiring a non-null value. The compiler currently uses this information to issue a warning when it detects a null value passed in such an argument slot.

ref. https://gcc.gnu.org/gcc-3.3/changes.html

자세한 내용은 gcc 문서에 친절하게 나와 있습니다.


gcc 7.3/Common-Function-Attributes/nonnull
nonnull (arg-index, …)

The nonnull attribute specifies that some function parameters should be non-null pointers. For instance, the declaration:
extern void *
    my_memcpy (void *dest, const void *src, size_t len)
    __attribute__((nonnull (1, 2)));
causes the compiler to check that, in calls to my_memcpy, arguments dest and src are non-null. If the compiler determines that a null pointer is passed in an argument slot marked as non-null, and the -Wnonnull option is enabled, a warning is issued. The compiler may also choose to make optimizations based on the knowledge that certain function arguments will never be null.

If no argument index list is given to the nonnull attribute, all pointer arguments are marked as non-null. To illustrate, the following declaration is equivalent to the previous example:
extern void *
    my_memcpy (void *dest, const void *src, size_t len)
    __attribute__((nonnull));
ref. https://gcc.gnu.org/gcc-3.3/changes.html

단순한 attribute이기 때문에 간단한 sample code로 이해할 수 있습니다.
중요한 것은 argument index list가 0-based가 아니라 1-based 라는 것입니다.

Check with code

-> sample source code: nonnull.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void __attribute__((nonnull(1, 2)))
  my_test_function(char *dest, const char *src, int len) {
	strncpy(dest, src, len);
}

int main(void) {
	my_test_function(NULL, NULL, 10);

	return 0;
}

my_test_function()에서 destsrc는 NULL이 되면 안 된다고 명시했습니다.

$ gcc -Wnonnull nonnull.c

nonnull.c: In function ‘main’:
nonnull.c:11:2: warning: null argument where non-null required
										(argument 1) [-Wnonnull]
  my_test_function(NULL, NULL, 10);
  ^~~~~~~~~~~~~~~~
nonnull.c:11:2: warning: null argument where non-null required
										(argument 2) [-Wnonnull]

compiler가 친절하게 argument 1, 2에는 NULL을 사용하면 안 된다고 warning 메시지를 줬습니다.
Compile warning은 가끔 실수로 지나칠 수 있으니, compile이 실패 발생하도록 error로 바꾸는 게 좋겠네요.

$ gcc -Werror=nonnull nonnull.c

nonnull.c: In function ‘main’:
nonnull.c:11:2: error: null argument where non-null required
										(argument 1) [-Werror=nonnull]
  my_test_function(NULL, NULL, 10);
  ^~~~~~~~~~~~~~~~
nonnull.c:11:2: error: null argument where non-null required
										(argument 2) [-Werror=nonnull]
cc1: some warnings being treated as errors

중요한 건, nonnull attribute을 사용했다고 해서, my_test_function() 함수 안에서 NULL check를 하지 않아도 된다는 뜻은 아닙니다.
이 attribute는 오직 compile-time에서 예측 가능한 상황에서만 동작합니다.
compile-time 검출의 한계이며 어찌 보면 당연한 한계 입니다.

-> Can not detect for the following situations:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void __attribute__((nonnull(1, 2)))
		my_test_function(char *dest, const char *src, int len) {
	strncpy(dest, src, len);
}

int main(void) {
	char *a = NULL;

	my_test_function(a, "test", 5);

	return 0;
}
$ gcc -Werror=nonnull nonnull.c
/* build success! */

위 상황은 compiler 입장에서 충분히 예측 가능한 NULL인데도 불구하고 검출을 못 하네요..

만약 nonnull attribute을 사용할 때, argument index list를 주지 않으면, 모든 argument에 대해서 nonnull check를 하게 됩니다.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void __attribute__((nonnull))
		my_test_function(char *dest, const char *src, int len) {
	strncpy(dest, src, len);
}

int main(void) {
	my_test_function(NULL, NULL, 5);

	return 0;
}
$ gcc -Werror=nonnull nonnull.c

nonnull.c: In function ‘main’:
nonnull.c:11:2: error: null argument where non-null required
										(argument 1) [-Werror=nonnull]
  my_test_function(NULL, NULL, 10);
  ^~~~~~~~~~~~~~~~
nonnull.c:11:2: error: null argument where non-null required
										(argument 2) [-Werror=nonnull]
cc1: some warnings being treated as errors

비록 검출의 한계가 있지만, user의 실수를 compile-time에 검출할 가능성이 있다는 것은 큰 이점으로 보입니다.
Compiler attribute는 Runtime overhead도 없기 때문에, 이러한 attribute을 많이 활용할 계획입니다.

jooojub.

gcc attribute: format, format_arg

The format attribute specifies that a function takes printf, scanf, strftime or strfmon style arguments which should be type-checked against a format string. Continue reading

gcc options: -Wformat

Published on November 28, 2020

gcc builtin: choose_expr

Published on September 15, 2019