Skip to content

Instantly share code, notes, and snippets.

@itamaro
Created May 21, 2023 18:42
Show Gist options
  • Save itamaro/bca973ae5cb962dec3a9704d0feea656 to your computer and use it in GitHub Desktop.
Save itamaro/bca973ae5cb962dec3a9704d0feea656 to your computer and use it in GitHub Desktop.
py-setproctitle find_argv_from_env heap buffer underflow repro
# build it with ASAN
clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer test.cpp -o test
# run without args
./test
argv = 0x7ffd68aa6f58 argv[0] = 0x7ffd68aa8dd5
environ = 0x7ffd68aa6f68:SHELL=/bin/bash at 89
environ = 0x7ffd68aa6f68 at 99
walking from environ to look for the arguments
environ = 0x7ffd68aa6f68 at 31
ptr = 0x602000000030:SHELL=0
found environ at 0x602000000030
argv[0] should be at 0x602000000029
=================================================================
==4060496==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000029 at pc 0x0000004a3e13 bp 0x7ffd68aa6db0 sp 0x7ffd68aa6560
READ of size 1 at 0x602000000029 thread T0
#0 0x4a3e12 in __interceptor_strcmp.part.0 (/data/users/itamaro/repro/test+0x4a3e12)
#1 0x620df7 in find_argv_from_env(int, char*) /data/users/itamaro/repro/test.cpp:70:9
#2 0x620b45 in main /data/users/itamaro/repro/test.cpp:101:16
#3 0x7fe82262c656 in __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#4 0x7fe82262c717 in __libc_start_main_impl /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:409:3
#5 0x421080 in _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:116
0x602000000029 is located 7 bytes to the left of 8-byte region [0x602000000030,0x602000000038)
allocated by thread T0 here:
#0 0x4c827f in malloc (/data/users/itamaro/repro/test+0x4c827f)
#1 0x7fe82264743b in __add_to_environ /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/stdlib/setenv.c:215:10
#2 0x620b01 in main /data/users/itamaro/repro/test.cpp:98:3
#3 0x7fe82262c656 in __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#4 0x7fe82262c717 in __libc_start_main_impl /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:409:3
#5 0x421080 in _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:116
SUMMARY: AddressSanitizer: heap-buffer-overflow (/data/users/itamaro/repro/test+0x4a3e12) in __interceptor_strcmp.part.0
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00 00 fa[fa]00 fa fa fa 00 00 fa fa fd fd
0x0c047fff8010: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8020: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8030: fa fa 00 04 fa fa 00 fa fa fa fd fd fa fa 00 00
0x0c047fff8040: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8050: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==4060496==ABORTING
./test 1
argv = 0x7ffe77590bc8 argv[0] = 0x7ffe77591dd3
environ = 0x7ffe77590be0:SHELL=/bin/bash at 89
environ = 0x7ffe77590be0 at 99
walking from environ to look for the arguments
environ = 0x7ffe77590be0 at 31
ptr = 0x602000000030:SHELL=0
found environ at 0x602000000030
=================================================================
==4062348==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000002f at pc 0x000000620daf bp 0x7ffe77590a20 sp 0x7ffe77590a18
READ of size 1 at 0x60200000002f thread T0
#0 0x620dae in find_argv_from_env(int, char*) /data/users/itamaro/repro/test.cpp:43:13
#1 0x620b45 in main /data/users/itamaro/repro/test.cpp:101:16
#2 0x7f8a33a2c656 in __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#3 0x7f8a33a2c717 in __libc_start_main_impl /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:409:3
#4 0x421080 in _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:116
0x60200000002f is located 1 bytes to the left of 8-byte region [0x602000000030,0x602000000038)
allocated by thread T0 here:
#0 0x4c827f in malloc (/data/users/itamaro/repro/test+0x4c827f)
#1 0x7f8a33a4743b in __add_to_environ /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/stdlib/setenv.c:215:10
#2 0x620b01 in main /data/users/itamaro/repro/test.cpp:98:3
#3 0x7f8a33a2c656 in __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#4 0x7f8a33a2c717 in __libc_start_main_impl /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:409:3
#5 0x421080 in _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:116
SUMMARY: AddressSanitizer: heap-buffer-overflow /data/users/itamaro/repro/test.cpp:43:13 in find_argv_from_env(int, char*)
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00 00 fa[fa]00 fa fa fa fd fd fa fa fd fd
0x0c047fff8010: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8020: fa fa fd fd fa fa fd fd fa fa fd fd fa fa 00 04
0x0c047fff8030: fa fa 00 fa fa fa fd fd fa fa 00 00 fa fa fd fd
0x0c047fff8040: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8050: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==4062348==ABORTING
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef ARG_MAX
#define ARG_MAX (96 * 1024)
#endif
extern "C" char **environ;
static char **
find_argv_from_env(int argc, char *arg0)
{
int i;
char **buf = NULL;
char **rv = NULL;
char *ptr;
char *limit;
printf("walking from environ to look for the arguments\n");
if (!(buf = (char **)malloc((argc + 1) * sizeof(char *)))) {
printf("can't malloc %d args!\n", argc);
//PyErr_NoMemory();
goto exit;
}
buf[argc] = NULL;
/* Walk back from environ until you find argc-1 null-terminated strings.
* Don't look for argv[0] as it's probably not preceded by 0. */
printf("environ = %p at %d\n", environ, __LINE__);
ptr = environ[0];
printf("ptr = %p:%s\n", ptr, ptr);
if (!ptr) {
/* It happens on os.environ.clear() */
printf("environ pointer is NULL\n");
goto exit;
}
printf("found environ at %p\n", ptr);
limit = ptr - ARG_MAX;
--ptr;
for (i = argc - 1; i >= 1; --i) {
if (*ptr) {
printf("zero %d not found\n", i);
goto exit;
}
--ptr;
while (*ptr && ptr > limit) { --ptr; }
if (ptr <= limit) {
printf("failed to found arg %d start\n", i);
goto exit;
}
buf[i] = (ptr + 1);
printf("found argv[%d] at %p: %s\n", i, buf[i], buf[i]);
}
/* The first arg has not a zero in front. But what we have is reliable
* enough (modulo its encoding). Check if it is exactly what found.
*
* The check is known to fail on OS X with locale C if there are
* non-ascii characters in the executable path. See Python issue #9167
*/
ptr -= strlen(arg0);
printf("argv[0] should be at %p\n", ptr);
if (ptr <= limit) {
printf("failed to found argv[0] start\n");
goto exit;
}
if (strcmp(ptr, arg0)) {
printf("argv[0] doesn't match '%s'\n", arg0);
goto exit;
}
/* We have all the pieces of the jigsaw. */
buf[0] = ptr;
printf("found argv[0]: %s\n", buf[0]);
rv = buf;
buf = NULL;
exit:
if (buf) { free(buf); }
return rv;
}
int main(int argc, char* argv[]) {
printf("argv = %p argv[0] = %p\n", argv, argv[0]);
printf("environ = %p:%s at %d\n", environ, environ[0], __LINE__);
char* dup = strdup(environ[0]);
char* tmp = dup;
while (*tmp++) {
if (*tmp == '=') {
*tmp = '\0';
break;
}
}
setenv(dup, "0", 1);
printf("environ = %p at %d\n", environ, __LINE__);
auto argv1 = find_argv_from_env(argc, argv[0]);
free(dup);
return argv1 ? 0:1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment