Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Very large python function definition in exec() call crashes Django but does not crash directly executed Python code

Tags:

python

django

I have a very large (~400k lines) Python function that I am attempting to define through an exec() call. If I run the following Python script:

exec("""def blah()
# 400k lines of IF/THEN/ELSE
""", globals())
blah()

By calling Python from the command line, it works fine.

However, if I do the same within a Django instance, it crashes the server without any error message or stack trace, which I can only assume is due to a segmentation fault.

Both Django runserver and the above script are run from the same Conda enviroment, and both have unlimited stack available (confirmed by printing out resource.getrlimit in Django).

Here's my full ulimit -a output:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 515017
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 4096
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

The command sequence to launch the Django server is as follows:

source activate <conda env name>
python manage.py runserver

This is the shell input/output leading to the crash:

(faf) [pymaster@t9dpyths3 faf]$ python manage.py runserver 9000
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
August 04, 2020 - 08:25:19
Django version 3.0.3, using settings 'faf.settings'
Starting development server at http://127.0.0.1:9000/
Quit the server with CONTROL-C.
[04/Aug/2020 08:25:25] "GET /projects/ HTTP/1.1" 200 13847
[04/Aug/2020 08:26:49] "PUT /projects/projectname/ HTTP/1.1" 200 76  # This event triggers the exec
(faf) [pymaster@t9dpyths3 faf]$
like image 988
Darrrrrren Avatar asked Jul 31 '20 13:07

Darrrrrren


3 Answers

The problem might be due to int(s), float(s) and others may cause segmentation fault

As mentioned here:

Please try setting the environmental flag PYTHONMALLOC=debug

This might allow your code to run without running into segmentation errors, if you do still get an error you should be able to catch it using.

PYTHONMALLOC=debug python3 -X tracemalloc=10

You might also want to check out: faulthandler

This module contains functions to dump Python tracebacks explicitly, on a fault, after a timeout, or on a user signal. Call faulthandler.enable() to install fault handlers for the SIGSEGV, SIGFPE, SIGABRT, SIGBUS, and SIGILL signals. You can also enable them at startup by setting the PYTHONFAULTHANDLER environment variable or by using the -X faulthandler command line option.

Adding this for more clarity since it's related; the following is taken from the answer provided by Darrrrrren and is a tweak to make faulthandler run on threaded django applications:

So I was able to get a stack trace by initializing Python with faulthandler, but additionally I had to run manage.py runserver --nothreading --noreload - for some reason if you do not disable threading with Django, even faulthandler will not print a stack trace.

like image 82
alexisdevarennes Avatar answered Oct 26 '22 17:10

alexisdevarennes


If you're using Python 2 (perhaps accidentally), you're simply passing too much to exec

You can reproduce this as follows (also see a relevant codegolf!)

% python2
>>> exec(
... """if True:
...     pass
... """ * (200 * 1000)  # 400k lines
... )
segmentation fault python2

You should be able to fix this by breaking it up (described in my other answer), or by writing the code to a file and importing it instead (as suggested/already implemented in comments)

This limit on exec should be fixed in Python 3 (RecursionError), but may affect a few unlucky versions (see ticket).

like image 3
ti7 Avatar answered Oct 26 '22 18:10

ti7


So I was able to get a stack trace by initializing Python with faulthandler, but additionally I had to run manage.py runserver --nothreading --noreload - for some reason if you do not disable threading with Django, even faulthandler will not print a stack trace.

Fatal Python error: Segmentation fault

Current thread 0x00007fe61836b740 (most recent call first):
  File "/apps/AADD/projects/FAF/Web App/faf/modelling/views.py", line 42 in index
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/base.py", line 113 in _get_response
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/utils/deprecation.py", line 94 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34 in inner
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/base.py", line 75 in get_response
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/handlers/wsgi.py", line 133 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/contrib/staticfiles/handlers.py", line 68 in __call__
  File "/apps/AADD/envs/faf/lib/python3.6/wsgiref/handlers.py", line 137 in run
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/servers/basehttp.py", line 197 in handle_one_request
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/servers/basehttp.py", line 172 in handle
  File "/apps/AADD/envs/faf/lib/python3.6/socketserver.py", line 724 in __init__
  File "/apps/AADD/envs/faf/lib/python3.6/socketserver.py", line 364 in finish_request
  File "/apps/AADD/envs/faf/lib/python3.6/socketserver.py", line 351 in process_request
  File "/apps/AADD/envs/faf/lib/python3.6/socketserver.py", line 320 in _handle_request_noblock
  File "/apps/AADD/envs/faf/lib/python3.6/socketserver.py", line 241 in serve_forever
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/servers/basehttp.py", line 216 in run
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 139 in inner_run
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 104 in run
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 95 in handle
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/base.py", line 369 in execute
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 60 in execute
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/base.py", line 328 in run_from_argv
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/__init__.py", line 395 in execute
  File "/apps/AADD/envs/faf/lib/python3.6/site-packages/django/core/management/__init__.py", line 401 in execute_from_command_line
  File "manage.py", line 17 in main
  File "manage.py", line 21 in <module>
Segmentation fault

If I provide unlimited stack space, the exec() actually works in Django, but only with --nothreading . So I have a hunch that Django is somehow restricting stack size to spawned off threads.

like image 1
Darrrrrren Avatar answered Oct 26 '22 19:10

Darrrrrren