1

From the HPUX cc, c89 - C compiler man page:

  -z             Do not bind anything to address zero.  This option
                 allows runtime detection of null pointers.  See the
                 note on pointers below.

  -Z             Allow dereferencing of null pointers.  See the note on
                 pointers below.  The -z and -Z are linker options.  See
                 ld(1) for more details.

Then from ld(1):

The default value of the -Z/-z option is -Z.

This means that, by default, any C program compiled with this version of cc on HPUX that dereferences a null pointer will read the value as 0 and not segfault. There is no option for this with gcc or cc on RHEL. Does anyone know how I would compile a C program with an option like this on RHEL (to allow null dereferece)? I know this is a terrible coding practice and I will not be using it to create new code. Thank you.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Kyle
  • 97
  • 5
  • 1
    Once upon a time, back when we were using [a.out](https://en.wikipedia.org/wiki/A.out) format executables, the answer to this question hinged on what "magic number" your executable had. There were 0407 executables, 0410, and perhaps a few others. The old format (0407) had page 0 mapped, meaning that null pointers were accessible. There were linker options (perhaps the same `-z` and `-Z` that HPUX is using, I don't remember) to say what kind of executable / which magic number you wanted. – Steve Summit Nov 13 '19 at 19:00
  • 1
    These days, [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) has almost completely supplanted a.out. I can easily imagine that there are different flavors of ELF (or more specifically, option bits in the ELF header) controlling things like whether page 0 should be accessible. I haven't had any luck finding any lists of such flavors, though. (For a.out, the flavors used to be listed either on the exec(2) or a.out(5) man pages, but I haven't found any modern Linux pages corresponding to those.) – Steve Summit Nov 13 '19 at 19:02
  • 1
    I just tried the mmap solution by @mevets at that [other question](https://stackoverflow.com/questions/58736028/c-strchr-works-with-null-value-on-hpux-but-segfaults-on-rhel) and it worked for me under Debian. Did you rule it out because it requires running as root? (If so I don't blame you.) – Steve Summit Nov 13 '19 at 19:14
  • @SteveSummit yes this is my followup to that question. root is an issue and RHEL actually blocks mmap, at least what I'm on. I should have mentioned that in the question. – Kyle Nov 13 '19 at 19:16
  • 1
    It is perhaps possible to do what you want with the help of linker scripts. See gnu ld manual for information about them. – AProgrammer Nov 13 '19 at 19:25
  • 1
    I'm no expert on ELF format, but I'm guessing it would be possible to create an ELF file that, besides all the normal code and data segments it typically sets up today, also created a page of 0's at address 0. So it would be possible to have an option to `ld` to make it do that. Or it would be possible to write a program to take an ELF file to modify it to add such a segment. There might even be an off-the-shelf ELF-manipulation tool that could do it. (Or, as AProgrammer wrote while I was writing this, a linker script.) – Steve Summit Nov 13 '19 at 19:26

1 Answers1

0

This is possible by adding some code to your program.

Linux kernels have a minimal mapped address that is generally > 0 to prevent null pointers from going undetected. They control this with system control vm.mmap_min_addr that is generally set to 4096 (32 bit) or 65536 (64 bit). Although it is not advised to do so, you can set this to using

sudo sysctl -w vm.mmap_min_addr=0

Or to set it permanently:

echo "vm.mmap_min_addr=0" > /etc/sysctl.d/mmap_min_addr.conf
/sbin/sysctl -p

Then you can add an empty memory page at virtual address 0:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

int main(int argc, char **argv) {
  int *p = NULL;
  int n;

  printf(" p = %p\n", p);

  printf(" map = %p\n", mmap(NULL, 4096, PROT_READ,
                             MAP_FIXED | MAP_ANON | MAP_PRIVATE, 0, 0));

  n = *p;

  printf("*p = %d\n", n);

  return 0;
}

Example runs:

$ ./ref0
p = (nil)
Segmentation fault (core dumped)
$ ./ref0 map
p = (nil)
map = (nil)
*p = 0

References:

fork2execve
  • 1,561
  • 11
  • 16