Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I goto into the scope of a alloca:d variable, but not a variable length array?

See this test program:

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

int main(int argc, char *argv[])
{
  if (argc < 2)
    goto end;

  char s[strlen(argv[1]) + 1];
  strcpy(s, argv[1]);
  printf("s=%s\n", s);

end:
  return 0;
}

It fails to compile with the error "jump into scope of identifier with variably modified type" (see other question).

However it compiles fine if I change the declaration of s into this (and include alloca.h):

char *s = alloca(strlen(argv[1]) + 1);

Why does the C standard allow jumping into the scope of an object created with alloca, but not a variable length array? I thought they were equivalent.

like image 471
Tor Klingberg Avatar asked Jun 02 '17 15:06

Tor Klingberg


2 Answers

It is because the compiler must runtime-initialize the frame of the scope with VLAs. In other words, you tell it to jump to address :END but you ask it to jump over the initialization code of the frame of that scope.

The code for initializing the space for the VLA is just before the expression that computes the length of the VLA. If you skip that code, which some goto can do, all the program will segfault.

Imagine something like:

if (cond) goto end;
...
char a[expr];
end:
a[i] = 20;

In this case the code will simply segfault, as you jump to the mutator of VLA a but a was not initialized. The code for initializing a VLA must be inserted in the place of the definition.

Now about alloca. The compiler will do the same, but it is not able to detect a segfault.

So this will segfault, with no warning/error from the part of the compiler.

The logic is the same as for VLA.

int main(int argc, char *argv[])
{
    goto end;
    char *s = alloca(100);
end:
    s[1] = 2;
    return 0;
}

In ISO 9899 this is why they inserted the statement:

6.8.6.1 The goto statement -- Constraints

1 The identifier in a goto statement shall name a label located somewhere in the enclosing function. A goto statement shall not jump from outside the scope of an identifier having a variably modified type to inside the scope of that identifier.

The compiler cannot detect during the static analysis the correct answer for this problem, as this is actually the halting problem.

like image 173
alinsoar Avatar answered Sep 23 '22 13:09

alinsoar


Besides the issue with deallocating the VLA if the program jumped after its declaration, there is also an issue with sizeof.

Imagine your program was extended with this:

end:
    printf("size of str: %zu\n", sizeof s);
    return 0;
}

For the alloca version, sizeof s == sizeof(char*), which can be computed at compile-time and all is well. However, for the VLA version, the length of s is unknown and sizeof s cannot be computed.

like image 27
Kninnug Avatar answered Sep 21 '22 13:09

Kninnug