5 ms·
`free(NULL);` will crash on some platforms that gcc supports, I believe.
by jcupitt 1y ago
`free(NULL);` will crash on some platforms that gcc supports, I believe.
- po1nt 1y agoIt shouldn't https://pubs.opengroup.org/onlinepubs/7908799/xsh/free.html https://pubs.opengroup.org/onlinepubs/7908799/xsh/free.html >If ptr is a null pointer, no action occurs.
- quietbritishjim 1y agoWhile I agree it shouldn't, that particular document is the UNIX specification, not the C specification, so it does not apply to C compilers on non-UNIX platforms.
- jibal 1y agofree(NULL) is a noop ever since C89 (I was on the standards committee, X3J11).
- mrheosuper 1y agocan we just do `if(*ptr == NULL) return;` ?
- menaerus 1y agoNo, because optimizing compilers are free to elide the check. https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fdelete-null-pointer-checks https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#ind...
- chongli 1y agoNot on all platforms! If you’re writing portable code targeting a lot of embedded platforms then you don’t want to rely on this optimization.
- menaerus 1y agoIt's a platform-agnostic optimization in case of GCC so if your embedded Linux toolchain is based on GCC, and most of them are, it's pretty much the case that it will have this optimization turned on by default. > This option is enabled by default on most targets. On AVR and MSP430, this option is completely disabled.
- chongli 1y agoYes and if you’re targeting AVR, an extremely popular 8 bit micro, then it’ll be turned off.
- mrheosuper 1y agoI'm not quite familar with this flag, but this >so that if a pointer is checked after it has already been dereferenced, it cannot be null. sound to me that if i've never deref the pointer anytime before(e.g the null check is at the beginning of function), the compiler won't remove this check.
- menaerus 1y agoSince the compiler will merge/fold what it appears to be a different logic sections of your code into a single one, you can never be sure what the release build codegen looks like unless you read the assembly.
- knorker 1y agoIf you check for null pointer before you dereference, then no the compiler cannot elide the check. If you check after dereferencing it, yes it can. But in this case why would you not check before dereferencing? It's the only UB-free choice.
- menaerus 1y agoYes, it can. Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all, so, compiler indeed can elide the nullptr check before dereferencing the ptr exactly because it is free to _always_ assume that the program is free of UB. To be more precise GCC says "eliminate useless checks for null pointers" and what I am saying that you can never be sure what in your code ended up being "useless check" vs "useful check" according to the GCC dataflow analysis. Linux kernel is a famous example for disabling this code transformation because it is considered harmful. And there's nothing harmful with the nullptr check from your example.
- knorker 1y ago> Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all Right. It's UB. And that's why the optimization in question is about removing that check. The only reason the optimization is valid for a C compiler to do, is that it can assume dereferencing a null pointer lands you in UB land. I'm sorry, either you are terrible at trying to explain things, or you have thoroughly misunderstood what all this is about. GCC cannot, under any circumstances or with any flags, remove an "if (ptr == NULL)" that happens before dereferencing the pointer. What this flag is about, and what the kernel bug you mentioned (at least I think you're referring to this one) is about, was a bug that went "int foo = ptr->some_field; […] if (ptr == NULL) { return -EINVAL; }". And GCC removed the post-deref null pointer check, thus making the bug exploitable. From the help text: > if a pointer is checked after it has already been dereferenced, it cannot be null. after. Only applies after. A check before dereferencing can never be removed by the compiler. Obviously.
- jibal 1y agoYes, but `*ptr == NULL` is just plain wrong ... it should be `ptr == NULL` ... but that test is redundant since `free` is required to do it.
- inkyoto 1y agoIf «ptr» is not a valid pointer, an attempt to dereference it (i.e. *ptr) will most assuredly crash the process with a SIGSEGV.
- knorker 1y agoBut when would it not be a valid pointer, and yet also not a null pointer? A null pointer we can check for easily.
- inkyoto 1y agoA null pointer is not a valid pointer in a predominant number of systems in existence. If malloc (3) has returned a NULL, *ptr will cause a SIGSEGV. Embedded systems are an exception, though. They may not have a MMU, and in such a case the operation will succeed.
- knorker 1y ago1. No, dereferencing a null pointer will not "cause a sigsegv". It causes UB. In practice, in unix user space, yes it'll probably be SIGSEGV. 2. A null pointer is not a valid pointer: Yeah… Once again my question was "But when would it not be a valid pointer, and yet also not a null pointer? A null pointer we can check for easily." This code will NEVER deference a null pointer. Not under any compiler, not with any compiler options: if (ptr != NULL) { *ptr = 0; } > A null pointer is not a valid pointer in a predominant number of systems in existence. No, that's not quite pedantically accurate. A null pointer is not a valid pointer in the C programming language. Address zero may or may not be, that's outside the scope of the C language. Which is why embedded and kernel work sometimes has to be very careful here. > They may not have a MMU, and in such a case the operation will succeed. Lack of MMU does not mean address zero is valid. It definitely* doesn't make a null pointer valid. In fact, a null pointer may not point to address zero.
- inkyoto 1y agoA zero (0, not NULL!) pointer is a valid pointer in C/C++. It is not a UB, and it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0». Old hardware designs used the address of 0 to store a jump address of the system boot-up sequence (i.e. firmware), and I personally wrote the code in C to inspect / use it in the unpriviledged hardware mode. The prevailing number of modern systems do not map the very first virtual (the emphasis is on virtual) memory page (the one that starts from zero) into the process address space for pragmatic reasons – an attempt to dereference a zero pointer is most assuredly a defect in the application. Therefore, an attempt to dereference a zero pointer always results in a page fault due to the zeroeth memory page not being present in the process' address space, which is always a SIGSEGV in a UNIX. Embdedded systems that do not have a MMU will allow *ptr where «ptr» is zero to proceed happily. Some (not all) systems may even have a system specific or a device register mapped at the address being 0. You are conflating several unrelated things, and there is no pedantry involved – it is a very simple matter with nothing else to debate.
- jibal 1y ago> can we just do `if(*ptr == NULL) return;` ? No, certainly not, but you can do `if(ptr == NULL) return;` which is correct but unnecessary since `free` is required to do that check.
- unwind 1y agoThat feels like a "citation needed", since that would be very clear violation of the C spec and thus a rather serious bug in the standard library for that platform.
- lelanthran 1y ago> `free(NULL);` will crash on some platforms that gcc supports, I believe. I'm pretty certain that `free(NULL)` is part of the C99 standard, so compiler vendors have had 25 years to address it. If your `free(NULL)` is crashing on a certain platform, you probably have bigger problems, starting with "Compiler that hasn't been updated in 25 years".
- jibal 1y agoIt's in C89 (I was on the standards committee, X3J11).
- SAI_Peregrinus 1y agoThen it's in violation of the C standard, at least as of C11 (I didn't check C99 or C89). > The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined. Emphasis mine
- jibal 1y ago> `free(NULL);` will crash on some platforms that gcc supports, I believe. No, of course it won't. `free(NULL)` has been a noop ever since C89 (and before, for that matter).