This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Long paths in imp.load_dynamic() lead to segfault
Type: crash Stage:
Components: Library (Lib) Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: tjollans, xxm
Priority: normal Keywords:

Created on 2021-04-06 07:11 by xxm, last changed 2022-04-11 14:59 by admin.

Messages (2)
msg390284 - (view) Author: Xinmeng Xia (xxm) Date: 2021-04-06 07:11
Long paths as arguments of imp.load_dynamic() lead to interpreter crashes.

Crash example
=====================================================
Python 3.10.0a2 (default, Nov 24 2020, 14:18:46)
[GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import imp
>>> imp.load_dynamic('',"abs/"*10000000)
Segmentation fault (core dumped)
======================================================

Environment:
Ubuntu 16.04, Python 3.92, Python 3.10.0a2
Mac OS Big Sur 11.2.3, Python 3.91, Python 3.10.0a2


Testing with gdb
-------------------------------------------------------------------------------------
$gdb ./python
(gdb) run 
Python 3.10.0a6 (default, Mar 19 2021, 11:45:56) [GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import imp
>>> imp.load_dynamic('','abs/'*100000000)
Program received signal SIGSEGV, Segmentation fault.
memcpy () at ../sysdeps/x86_64/multiarch/../memcpy.S:272
272 ../sysdeps/x86_64/multiarch/../memcpy.S: No such file or directory.




Testing with valgrind
-------------------------------------------------------------------------------------
xxm@xxm-System-Product-Name:~$ PYTHONMALLOC=malloc_debug valgrind '/home/xxm/Desktop/apifuzz/Python-3.10.0a6/python'
==4923== Memcheck, a memory error detector
==4923== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4923== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==4923== Command: /home/xxm/Desktop/apifuzz/Python-3.10.0a6/python
==4923==
Python 3.10.0a6 (default, Mar 19 2021, 11:45:56) [GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.dgettext('abs'*10,'')
''
>>> import imp
<stdin>:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
>>> imp.load_dynamic('','abs/'*100000000)
==4923== Warning: set address range perms: large range [0x8037040, 0x1fdaf489) (undefined)
==4923== Warning: set address range perms: large range [0x1fdb0040, 0x37b28479) (undefined)
==4923== Warning: set address range perms: large range [0x37b29040, 0x4f8a1441) (undefined)
==4923== Warning: set address range perms: large range [0x37b29028, 0x4f8a1459) (noaccess)
==4923== Warning: set address range perms: large range [0x59eb3040, 0x71c2b460) (undefined)
==4923== Warning: client switching stacks? SP change: 0x1ffeffe460 --> 0x1fe7286028
==4923== to suppress, use: --max-stackframe=400000056 or greater
==4923== Invalid write of size 8
==4923== at 0x401513F: _dl_open (dl-open.c:701)
==4923== Address 0x1fe7286028 is on thread 1's stack
==4923==
==4923==
==4923== Process terminating with default action of signal 11 (SIGSEGV)
==4923== Access not within mapped region at address 0x1FE7286028
==4923== at 0x401513F: _dl_open (dl-open.c:701)
==4923== If you believe this happened as a result of a stack
==4923== overflow in your program's main thread (unlikely but
==4923== possible), you can try to increase the size of the
==4923== main thread stack using the --main-stacksize= flag.
==4923== The main thread stack size used in this run was 8388608.
==4923== Invalid write of size 8
==4923== at 0x4A2867A: _vgnU_freeres (vg_preloaded.c:57)
==4923== Address 0x1fe7286020 is on thread 1's stack
==4923==
==4923==
==4923== Process terminating with default action of signal 11 (SIGSEGV)
==4923== Access not within mapped region at address 0x1FE7286020
==4923== at 0x4A2867A: _vgnU_freeres (vg_preloaded.c:57)
==4923== If you believe this happened as a result of a stack
==4923== overflow in your program's main thread (unlikely but
==4923== possible), you can try to increase the size of the
==4923== main thread stack using the --main-stacksize= flag.
==4923== The main thread stack size used in this run was 8388608.
==4923==
==4923== HEAP SUMMARY:
==4923== in use at exit: 1,205,374,369 bytes in 36,250 blocks
==4923== total heap usage: 96,421 allocs, 60,171 frees, 1,616,393,081 bytes allocated
==4923==
==4923== LEAK SUMMARY:
==4923== definitely lost: 0 bytes in 0 blocks
==4923== indirectly lost: 0 bytes in 0 blocks
==4923== possibly lost: 805,237,234 bytes in 35,439 blocks
==4923== still reachable: 400,137,135 bytes in 811 blocks
==4923== suppressed: 0 bytes in 0 blocks
==4923== Rerun with --leak-check=full to see details of leaked memory
==4923==
==4923== For lists of detected and suppressed errors, rerun with: -s
==4923== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
msg395107 - (view) Author: Thomas Jollans (tjollans) Date: 2021-06-04 18:13
I cannot reproduce this on my OpenSUSE (glibc 2.33, Linux 5.12.4) or Ubuntu 20.04 (glibc 2.31, Linux 5.4.0) machines, but I can reproduce it on an old Debian Stretch VM I happened to have lying around (glibc 2.24, Linux 4.9.0). (FreeBSD 12.2 and Windows 10 also fine.)

This doesn't look like a bug in Python, but like a bug in glibc (and Apple's libc?) (or Linux?) that is fixed in current versions.

This C program produces the same result - segfault on old Linux, error message on new Linux.

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

static const char *FRAGMENT =  "abs/";
#define REPEATS 10000000

int main()
{
        size_t fragment_len = strlen(FRAGMENT);
        size_t len = fragment_len * REPEATS;
        char *name = malloc(len + 1);
        name[len] = '\0';
        for (char *p = name; p < name + len; p += fragment_len) {
                memcpy(p, FRAGMENT, fragment_len);
        }
        
        void *handle = dlopen(name, RTLD_LAZY);
        if (handle == NULL) {
                printf("Failed:\n%s\n", dlerror());
                free(name);
                return 1;
        } else {
                printf("Success.");
                dlclose(handle);
                free(name);
                return 0;
        }
}
History
Date User Action Args
2022-04-11 14:59:43adminsetgithub: 87906
2021-06-04 18:13:41tjollanssetnosy: + tjollans
messages: + msg395107
2021-04-06 07:11:07xxmcreate