This is the mail archive of the gcc-help@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Possible explanation why no segfault with -fno-delete-null-pointer-checks and -O2?Possible explanation why no segfault with -fno-delete-null-pointer-checks and -O2?


Hi,

I can't understand why doesn't the following piece of code end with
segfault while dereferencing a NULL pointer in line 29 but only when
compiled with `-fn-delete-null-pointer-checks' and `-02':

#include <stdio.h>

struct agnx_priv {
        char demo;
};

struct ieee80211_hw {
        struct agnx_priv *priv;
};

struct pci_dev {
        struct ieee80211_hw* dev;
};

struct ieee80211_hw* pci_get_drvdata(struct pci_dev *pdev) {

        if(!pdev)
                return NULL;

        return pdev->dev;
}

static void agnx_pci_remove (struct pci_dev *pdev)
{
        printf("pdev value: %p\n", pdev);
        int a = 700;
        struct ieee80211_hw *dev = pci_get_drvdata(pdev);
        printf("dev value: %p\n", dev);
        struct agnx_priv *priv = dev->priv;
        printf("past the dereferenc: %p\n", dev);

        if (!dev) return;

        // This will segfault if the previous if is optimized
        printf("%c\n", priv->demo);
}

int main(int argc, char **argv)
{
        // (struct pci_dev *)argv[1] is just to avoid compiler optimisations
        // =~ NULL if no args are passed.

        struct pci_dev * pdev = (struct pci_dev *)argv[1];

        agnx_pci_remove(pdev);
        return 0;
}


$ gcc -O2 -fno-delete-null-pointer-checks POST.c -o POST
$ ./POST
pdev value: (nil)
dev value: (nil)
past the dereferenc: (nil)
$ gcc -O2 POST.c -o POST
$ ./POST
pdev value: (nil)
dev value: (nil)
Segmentation fault
$ gcc -fno-delete-null-pointer-checks POST.c -o POST
$ ./POST
pdev value: (nil)
dev value: (nil)
Segmentation fault

Basically I started reading up on `-fno-delete-null-pointer-checks'
option after I learned it's used to compile Linux kernel. I read that
in case of kernel there is no segfault when attempting to dereference
a NULL pointer but a special handler named kernel oops instead comes
in place. This is why one may want to use
`-fno-delete-null-pointer-checks' when compiling kernel to make sure
that there won't be any NULL checks ignored even after erroneously
dereferencing a pointer it before checking with combination with
`-O2'. However, in userlands program it's common for a program to
crash and emit signal 11. I know that dereferencing a NULL pointer is
an UB according to C standard. I am wondering though if anybody can
point any technical reasons why doesn't the code I posted result in
segfault. What is more strange, it ends with segfault when
dereferencing a NULL pointer right away inside main():

int main(int argc, char **argv)
{
        // (struct pci_dev *)argv[1] is just to avoid compiler optimisations
        // =~ NULL if no args are passed.

        struct pci_dev * pdev = (struct pci_dev *)argv[1];
        printf("pdev value is %p\n", pdev);

        struct ieee80211_hw *dev = pci_get_drvdata(pdev);
        printf("before dereferencing\n");
        struct agnx_priv *priv = dev->priv;
        printf("%c\n", priv->demo);

        agnx_pci_remove(pdev);
        return 0;
}

$ gcc -fno-delete-null-pointer-checks POST.c -o POST
$ ./POST
pdev value: (nil)
dev value: (nil)
Segmentation fault

I spent a couple of hours looking for an explanation but my GCC-fu is too poor.

I tried to have fun and write another program on which I could use
`-O2 -fno-delete-null-pointer-checks' and see in practice that
subsequent NULL checks are ignored after first successful
dereferencing but can't write any in which I am able to get past first
NULL pointer dereferencing such as this one which when run with
parameter 0 already crashes at line 7:

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

int main(int argc, char *argv[])
{
        int *p = (int *) atoi(argv[1]);
        int i = *p;

        if (!p)
                {
                        printf("exiting\n");
                        exit(1);
                }

        printf("%d\n", i);

        exit(0);
}

My OS is Linux Slackware with kernel 3.2.29-smp and GCC version is 4.7.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]