Skip to content

Segfault during garbage collection with GzipFile + failed urllib3 request on 3.12.0 #111049

Closed
@basepi

Description

@basepi

Crash report

What happened?

import urllib3
import gzip
import io
import json
import faulthandler

faulthandler.enable()

def test():
    buffer = gzip.GzipFile(fileobj=io.BytesIO(), mode="w", compresslevel=5)
    fileobj = buffer.fileobj  # get a reference to the fileobj before closing the gzip file
    buffer.close()
    data = fileobj.getbuffer()
    headers = {}
    try:
        urllib3.request("POST", "http://127.0.0.1:8200/intake/v2/events")
    except Exception as e:
        print(e)

test()

The above example requires urllib3, so you'll need to install that first.

When the above example is run on Python 3.12.0, it results in a segfault:

HTTPConnectionPool(host='127.0.0.1', port=8200): Max retries exceeded with url: /intake/v2/events (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x1053c5280>: Failed to establish a new connection: [Errno 61] Connection refused'))
Fatal Python error: Segmentation fault

Current thread 0x00000001e1499300 (most recent call first):
  Garbage-collecting
  <no Python frame>
[1]    38085 segmentation fault  python test.py

Some weird things:

  • If I remove the urllib3.request, it doesn't segfault.
  • If the urllib3.request succeeds (in this case, if I run the Elastic APM Server locally), it doesn't segfault.
  • If I pull the code out of the function and run it flat, it doesn't segfault 🤯:
import urllib3
import gzip
import io
import json
import faulthandler

faulthandler.enable()

buffer = gzip.GzipFile(fileobj=io.BytesIO(), mode="w", compresslevel=5)
fileobj = buffer.fileobj  # get a reference to the fileobj before closing the gzip file
buffer.close()
data = fileobj.getbuffer()
headers = {}
try:
    urllib3.request("POST", "http://127.0.0.1:8200/intake/v2/events")
except Exception as e:
    print(e)

This is an extremely simplified version of the code where I first saw the segfault. Note that you don't even have to send the data into the urllib3.request to cause the issue. You don't even have to write anything to the buffer! Note, writing to the buffer does not prevent the segfault.

I can reproduce this issue on cpython 3.12.0 (built via pyenv) locally on macOS and on the python:3.12.0 docker image.

The segfault does not happen on 3.11 and below.

CPython versions tested on:

3.12, 3.11 (only happens on 3.12)

Operating systems tested on:

Linux, macOS

Output from running 'python -VV' on the command line:

Python 3.12.0 (main, Oct 2 2023, 17:34:07) [Clang 15.0.0 (clang-1500.0.40.1)]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixes3.13bugs and security fixestopic-IOtype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions