Skip to content

Invalid memcpy call #4319

Open
Open
@jakub-zwolakowski

Description

@jakub-zwolakowski

Analysis of JerryScript test test-literal-storage.c using TrustInSoft CI has shown that the function lit_is_utf8_string_magic (in file lit-magic-strings.c) sometimes calls memcpy with a null pointer as argument:
https://github.com/jakub-zwolakowski/jerryscript/blob/90c97fee6ca924a7c6bf42e07705cbe720ac89c0/jerry-core/lit/lit-magic-strings.c#L218

Passing a null pointer to a C library function like memcpy is dangerous, even if done together with the size argument equal zero.

Some compilers perform optimizations based on the assumption that such a function never receives a null pointer. GCC started to optimize this behavior aggressively in its 4.9 version with the optimization -fdelete-null-pointer-checks (it is discussed in this blog post: http://blog.mycre.ws/articles/bind-and-gcc-49/). This is an example of a program that passes a null pointer to memcpy:

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

__attribute__((noinline))
void f(char *p, int n) {
  if (n > 10) abort();
  memcpy(p, "0123456789", n);
  if (p == NULL) {
    printf("f was called with NULL\n");
  }
  else {
    printf("f was called with a non-NULL pointer\n");
    printf("this is the value of p:%p or 0x%llx\n", p, (unsigned long long) p);
  }
}

int main(void) {
  f(NULL, 0);
}

Binaries produced when compiling this program with GCC 7.5.0 print different results when run with different optimization settings:

$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc test.c -O0 && ./a.out 
f was called with NULL
$ gcc test.c -O2 && ./a.out 
f was called with a non-NULL pointer
this is the value of p:(nil) or 0x0

(You can see how the generated code looks like in Compiler Explorer for these two cases in GCC 10.2: with optimization level 0 and with optimization level 2. Note that in the latter case the whole p == NULL check disappears.)

This is not a GCC bug. This behavior is present in GCC already for several versions. What the compiler does is legal as the program passes a null pointer to memcpy which is forbidden by clause 7.1.4:1 in C17 (and this clause is already present in C99):

Each of the following statements applies unless explicitly stated otherwise in the detailed descrip-
tions that follow: If an argument to a function has an invalid value (such as a value outside the
domain of the function, or a pointer outside the address space of the program, or a null pointer, or a
pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a
type (after promotion) not expected by a function with variable number of arguments, the behavior
is undefined.

The program's behavior is undefined, so GCC can rightfully do whatever it finds most appropriate. And what it finds most appropriate (for optimization reasons) in this case is to suppose that any pointer p that was passed as an argument to memcpy is non-null.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions