Skip to content

Reject float format precision near INT_MAX #158446

Closed

Description

@gpshead

Bug report

(edit: description added now that the fix has been released)

Formatting a float with 'f' and a precision just below INT_MAX writes up to ~280 value-chosen digit bytes past a 28-byte buffer in dtoa.c. The only precondition is untrusted control of a float precision, %-template or format spec -- inputs applications commonly accept because %-formatting is assumed inert. The demonstrated result is a crash, but the byte-exact overflow length permits partial pointer overwrites into interpreter state, so we would not assume it stops there. The fix is a small precision bound check in PyOS_double_to_string() raising ValueError, as the format parsers already do above INT_MAX.

Workarounds

applications running on unpatched Pythons can work around this by sanitizing format strings. But doing so correctly without leaving gaps is hard. I'm not sure this list is actually exhaustive or accurate enough -gpshead:

  • If users choose a precision / "decimal places" value, bound it before it reaches formatting (p = min(int(p), 1000) or reject). The dangerous band starts at INT_MAX - 309; nothing legitimate needs more than a few hundred.
  • If users (or translators, theme/report authors, etc.) supply %-style templates, or standalone format specs (format(x, spec), f'{x:{spec}}'), that are applied to floats: reject any containing a run of 10 or more decimal digits, and reject * in %-templates. Match with \d on str, not [0-9]: the format-spec parser accepts any Unicode decimal digit (format(1.5, '.214748364\uff17e') is parsed as precision INT_MAX), while the %-parsers accept ASCII only. re.search(r'\d{10}', template) over the whole string is sufficient for these two cases, and unlike "parse the directives with a regex" guards it is immune to %(key)s / nested-parenthesis parsing subtleties.
  • That check is NOT sufficient for user-authored str.format templates: nested replacement fields inside a format spec can assemble the digits from fill characters or arguments ('{0:.{0!s:2>1.0}{0!s:1>1.0}...f}'), so no digit run ever appears in the template. Additionally reject any { inside a format spec, or better, do not accept such templates at all: they can also read attributes of the arguments. Prefer string.Template or an allow-listed spec grammar.
  • Running untrusted Python code in-process is out of scope for CPython's security model; use OS/process isolation.

Credit

Discovered by Peter Bailis, in collaboration with Claude and Anthropic Research.

CPython versions tested on:

3.10, 3.15, 3.11, 3.12, CPython main branch, 3.14, 3.13

Operating systems tested on:

No response

Linked PRs

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. added
    triagedThe issue has been accepted as valid by a triager.
    and removed
    type-bugAn unexpected behavior, bug, or error
    on Sep 29, 2026
  3. added 3 commits that reference this issue on Sep 30, 2026
  4. added
    3.11only security fixes
    3.12only security fixes
    3.13only security fixes
    on Sep 30, 2026
  5. 2 remaining items

  6. added a commit that references this issue on Sep 30, 2026
  7. added a commit that references this issue on Sep 30, 2026
  8. added a commit that references this issue on Sep 30, 2026
  9. removed
    3.13only security fixes
    3.14bugs and security fixes
    3.15bugs and security fixes
    on Sep 30, 2026
  10. changed the title [-]placeholder for a security issue[/-] [+]Use-after-free for server-side deallocated SSLContext with sni_callback[/+] on Sep 30, 2026
  11. added a commit that references this issue on Sep 30, 2026
  12. changed the title [-]Use-after-free for server-side deallocated SSLContext with sni_callback[/-] [+]Reject float format precision near INT_MAX[/+] on Sep 30, 2026
  13. added 2 commits that reference this issue on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions