A string literal like "/bin/sh" is stored in .rodata, which is mapped
read-only (see where things live in a C program).
But its C type is plain char[N], not const char[N] (C++ fixed this, C
kept it for historical reasons). So the compiler happily lets you do this:
char *p = "/bin/sh"; // compiles without a warning by default
p[0] = 'X'; // undefined behavior; in practice SIGSEGV
$ ./ub
Segmentation fault (exit status 139 = 128 + SIGSEGV)
-Wwrite-strings: make the compiler tell you
-Wwrite-strings gives literals the type const char[N], so pointing a plain
char * at one becomes a warning (an error with -Werror):
scratch.c:3:13: error: initialization discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]
3 | char *s = "scratching surface";
| ^~~~~~~~~~~~~~~~~~~~
The fix is to say what you mean: const char *s = "...";. It’s not part of
-Wall or -Wextra; you have to add it yourself.
Pointer to a literal vs. array initialized from one
char *s = "/bin/sh"; // A
char s[] = "/bin/sh"; // B
They look almost identical but are very different:
- A is a pointer. It points at the literal itself, in
.rodata. With-Wwrite-stringsthis is the error above. - B is an array of 8
chars (7 +'\0'). The literal is only its initializer: the bytes are copied into the array. No pointer, noconstdropped, and the array is yours to modify.
The variable s | The bytes "/bin/sh" | s[0] = 'X' | |
|---|---|---|---|
| A (local) | 8-byte pointer on the stack | the literal in .rodata | UB / SIGSEGV |
| B (local) | the array itself, on the stack | a copy, inside the array | fine |
B (static or global) | the array itself, in .data | a copy, inside the array | fine |
More differences:
- A can be repointed (
s = "other";). B can’t: arrays aren’t assignable, only their contents are. sizeof sis the pointer size for A and the array size for B. For"/bin/sh"both happen to be 8 on a 64-bit machine; use a longer string to see them differ.
The real question isn’t “binary or stack”, it’s “am I pointing at the literal, or do I own a copy?”
References: the C23 standard, sections “String literals” and “Initialization”;
man gcc (-Wwrite-strings).