Skip to content

Fix segfault in Record.get() with invalid positional argument count - #1333

Merged
elprans merged 1 commit into
MagicStack:masterfrom
apoorvdarshan:fix-record-get-segfault-1328
Sep 21, 2026
Merged

elprans merged 1 commit into
MagicStack:masterfrom
apoorvdarshan:fix-record-get-segfault-1328

Conversation

@apoorvdarshan

Copy link
Copy Markdown
Contributor

Summary

asyncpg.Record.get() could crash the interpreter (SEGV) — or, depending on stack contents, silently return the default instead of raising TypeError — when called with an invalid number of positional arguments, e.g. r.get() or r.get("a", 2, 3).

In record_get() (asyncpg/protocol/record/recordobj.c), the wrong-argument-count else branch sets a TypeError via PyErr_Format() but does not return. Execution falls through to:

res = record_item_by_name((ApgRecordObject *)self, key, &val);

with key left uninitialized, which is undefined behavior — a native crash on release builds. The release build also emitted warning: 'key' may be used uninitialized [-Wmaybe-uninitialized].

Fix

Return NULL immediately after setting the argument-count error:

} else {
    PyErr_Format(PyExc_TypeError,
                 "Record.get() expected 1 or 2 arguments, got %zd",
                 nargs);
    return NULL;
}

Tests / Verification

  • Added TestRecord::test_record_get_invalid_args in tests/test_record.py asserting TypeError for r.get(), r.get("a", 2, 3), and r.get(default=2).
  • Confirmed the new test fails on pristine master (AssertionError: TypeError not raised) and passes after the fix.
  • Built the C extension locally (python setup.py build_ext --inplace) and ran the full tests/test_record.py module: 26 passed. The rebuild no longer emits the 'key' may be used uninitialized warning.
  • Verified the issue's reproducer now raises the expected error:
    Record.get() expected 1 or 2 arguments, got 0
    Record.get() expected 1 or 2 arguments, got 3
    
    while valid calls (r.get("a"), r.get("a", default)) are unchanged.

Fixes #1328.

Disclosure: prepared with AI assistance; reviewed and verified locally.

record_get() set a TypeError with PyErr_Format() when given the wrong
number of positional arguments but did not return, falling through to
record_item_by_name() with an uninitialized `key` pointer. Depending on
the stack contents this either crashed the interpreter (SEGV) or silently
returned the default instead of raising TypeError. The release build also
emitted a `'key' may be used uninitialized` warning.

Return NULL immediately after setting the error, and add regression
coverage for the invalid argument-count cases.

Fixes MagicStack#1328.
@elprans elprans closed this Sep 21, 2026
@elprans elprans reopened this Sep 21, 2026
@elprans
elprans enabled auto-merge (squash) September 21, 2026 05:28

@elprans elprans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@elprans
elprans merged commit 40a3832 into MagicStack:master Sep 21, 2026
47 checks passed
@elprans elprans mentioned this pull request Oct 6, 2026
MaciejZet pushed a commit to MaciejZet/asyncpg that referenced this pull request Oct 8, 2026
## Improvements

* Support Python 3.15 (MagicStack#1367, MagicStack#1378)
  (by @elprans in 1b11846 and 58c0c59)

* Support PostgreSQL 19 (MagicStack#1379)
  (by @elprans in 8d2a5ae)

* Make pool `min_size` a maintained connection floor and add `init_size` to
  control the number of connections opened at startup (MagicStack#1323).
  Idle connections at or below `min_size` are retained, and closed connections
  are replaced in the background. `init_size` defaults to `min_size`.
  To preserve the previous behavior, where the pool could drain to zero,
  replace `min_size=N` with `init_size=N, min_size=0`.
  (by @shatilov-diman in 67a1cbc)

* Use `hashlib.pbkdf2_hmac()` for faster SCRAM password derivation (MagicStack#1359)
  (by @twelfthlabor in 00319a9)

* Use the statement cache for `copy_records_to_table()` introspection (MagicStack#1360)
  (by @matemax in dd05865)

## Fixes

* Clean up connections rejected by `target_session_attrs` immediately so an
  unresponsive rejected host cannot delay the selected connection, and cleanup
  tasks cannot outlive the event loop. Rejected connections are aborted without
  waiting for a graceful disconnect; servers may log "unexpected EOF on client
  connection" if the Terminate message is not delivered before the transport
  closes.

* Fix a connection pool deadlock when releasing a connection after a protocol
  abort (MagicStack#1324)
  (by @pchaseh in 4a83cc2)

* Honor `Connection.close(timeout=...)` while waiting for a pending
  cancellation (MagicStack#1361)
  (by @gyanu2507 in c06bcb4)

* Prevent stuck cancellation from blocking connection teardown or pool release
  by bounding cancellation with the command or release timeout (MagicStack#1370)
  (by @elprans in 4cf0220)

* Preserve the server error when the connection closes mid-operation (MagicStack#1369)
  (by @RitiGrover in 6c8f1a7)

* Fix COPY cleanup when a records generator raises, preserving the original
  exception and avoiding protocol desynchronization and stalled rollback (MagicStack#1358)
  (by @CodingCossack in fdc6bc8)

* Reparse unnamed statements before cursor binding when the statement cache
  is disabled (MagicStack#1364)
  (by @elprans in 302ba49)

* Allow cursors in transactions started outside `Connection.transaction()`,
  such as with an explicit `BEGIN` (MagicStack#1350)
  (by @aryansk in 25aaaf5)

* Remove the query logger when the `query_logger()` block raises (MagicStack#1373)
  (by @kratos0718 in 6a7fbd3)

* Fix a segfault in `Record.get()` with an invalid number of positional
  arguments (MagicStack#1333)
  (by @apoorvdarshan in 40a3832)

* Align SSL startup retries with libpq and improve SSL rejection diagnostics
  (MagicStack#1366). Retry alternative transports only before authentication succeeds.
  Direct TLS now requires `sslmode=require`, `verify-ca`, or `verify-full`,
  or an explicit `SSLContext`.
  (by @elprans in 78752c9)

* Preserve password errors during SSL fallback when the fallback fails with
  a less specific authorization error (MagicStack#1331)
  (by @Mauricio0129 in 2d97947)

* Handle cleartext password authentication when no password is supplied
  without raising an `AttributeError` (MagicStack#1351)
  (by @aryansk in 6be5915)

* Set the `postgresql` ALPN protocol on SSL contexts created by asyncpg so
  direct TLS connections work with PostgreSQL 17 and later (MagicStack#1372).
  User-provided contexts must set this protocol for direct TLS connections.
  (by @DurandA in b21325d)

* Link explicitly against `libm` on non-Windows platforms to fix undefined
  math symbol errors when loading the extensions (MagicStack#1305)
  (by @veeceey in db8ecc2)

* Fix builds on FreeBSD 15 by linking against `libstdthreads` (MagicStack#1368)
  (by @elprans in 87bcaf9)

## Other

* Replace `pkg_resources` with `packaging` in `setup.py` (MagicStack#1362)
  (by @elprans in 6695db7)

* Build Linux and macOS ARM64 wheels on native runners and add Windows ARM64
  wheel builds (MagicStack#1308)
  (by @ddelange in 1e8cc1c)

* Add manual and PR-label-triggered dry runs to the release workflow (MagicStack#1374)
  (by @elprans in 1bd643e)

* Clarify how `execute()` uses prepared statements and how to confirm an
  `INSERT` in the documentation (MagicStack#1336)
  (by @pctablet505 in cdf816c)

* Fix typos in comments and docstrings (MagicStack#1332)
  (by @maxtaran2010 in bc3e246)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants