GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-96512: Modernize int_max_str_digits protection in 3.12 by gpshead · Pull Request #96568 · python/cpython

Conversation

@gpshead

@gpshead gpshead commented Sep 5, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This takes care of the TODOs outlined in the linked issue:

  • Cleanup: Move int_max_str_digits into PyConfig in 3.12.
  • Use int32_t or Py_ssize_t instead of int for the max digits value internally.
  • Minor feature: add PEP-578 PySys_Audit hook calls at the new ValueError raise spots.
  • Update the version notes about to say 3.11 and remove the 3.12 What's New and NEWS entry text about it as this shipped in 3.11. The original PR was created not knowing exactly when we'd merge.
  • Issue: main branch code cleanups for int_max_str_digits non-backportable TODOs #96512

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
gpshead added 4 commits September 4, 2022 22:01
@gpshead
Revert "Undo addition to PyConfig to ease backporting."
25aa726
This reverts commit 1ad88f5.
@gpshead
Pedantically use int32_t instead of int for max.
67c9af4
@gpshead
Generate audit events on >threshold conversions.
c6e7f33
@gpshead
versionadded/changed to 3.11; update What's New.
e51cb02
@gpshead gpshead added type-feature A feature request or enhancement tests Tests in the Lib/test dir DO-NOT-MERGE interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Sep 5, 2022
@bedevere-bot bedevere-bot added the awaiting core review label Sep 5, 2022
@gpshead gpshead self-assigned this Sep 5, 2022
gpshead added 2 commits September 15, 2022 15:26
@gpshead
Merge remote-tracking branch 'upstream/main' into cleanup/int-dos
65fa579
@gpshead
Fix missing : in new docs typo.
fe9f4d0
@gpshead gpshead added skip news and removed skip news labels Sep 15, 2022
@gpshead
NEWS entry about the audit hooks added.
38077ca
@gpshead
gpshead marked this pull request as ready for review September 15, 2022 23:24
@gpshead gpshead removed the DO-NOT-MERGE label Sep 15, 2022
@gpshead
gpshead requested review from mdickinson and vstinner September 15, 2022 23:24
vstinner
vstinner requested changes Sep 16, 2022
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I don't get the rationale for audit events on operations which can be slow. Is that a new trend? If you consider that it's so important to emit audit events on "slow" operations, why not adding on every single Python operation which is "slow"? Like... time.sleep(), 10 ** (10 ** 10), or well... anything else.

It's hard for me to see how converting a large integer to a string, or a long string to an integer, is a threat. There is already the ValueErrror if it's larger than the limit.

Calling PySys_Audit() is cheap but it's not free. It has a minor impact on performance. Once it's added, it will hard to justify to remove it to "optimize Python". So I would prefer that we think twice about it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-bot bedevere-bot removed the awaiting core review label Sep 16, 2022
@bedevere-bot

bedevere-bot commented Sep 16, 2022

Copy link
Copy Markdown

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot added the awaiting changes label Sep 16, 2022
@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

Use int32_t or Py_ssize_t instead of int for the max digits value internally.

What's the rationale for that? It makes this specific option special and requires special code in Python/initconfig.c and other places. I would prefer to use an int for all integer options. If you want to put min or max values, you can add debug checks in config_check_consistency() and checks in _PyConfig_Read().

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Sep 16, 2022
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

This PR is hard to review, it does many things at once:

  • Add PyConfig.int_max_str_digits: you should add tests
  • Add a new int32_t type for PyConfig
  • Add new audit events
  • Update the int_max_str_digits documentation

You might split it into separated PR, like move the addition of audit events to a new PR.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Python/initconfig.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/initconfig.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Sep 16, 2022
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

With the adding of PyConfig.int_max_str_digits, IMO PyInterpreterState.int_max_str_digits becomes redundant and can introduce inconsistencies. I suggest removing it.

In PyInterpreterState, PyInterpreterState.config is a directly structure, not a pointer. At the machine code level, reading PyInterpreterState.config.int_max_str_digits is efficient: the offset in the memory is constant.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Doc/c-api/init_config.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/c-api/init_config.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/initconfig.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/initconfig.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

nitpick: in the commit message, you should mention that the change also removes _Py_global_config_int_max_str_digits.

I suggest to start with a PR with replaces _Py_global_config_int_max_str_digits + PyInterpreterState.int_max_str_digits with PyConfig.int_max_str_digits.

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gpshead gpshead added the DO-NOT-MERGE label Sep 16, 2022
@gpshead

gpshead commented Sep 16, 2022

Copy link
Copy Markdown
Member Author

Thanks! Yep this is entirely reasonable to split up into separate PRs.

The audit hook can go away, it was just an idea. I've personally never used those.

I used individual commits for each list item to make wrangling this into smaller PRs after concept review easy, will do.

Using int32_t was a request from @mdickinson on the original PR.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

Using int32_t was a request from @mdickinson on the original PR.

Objects/longobject.c uses int max_str_digits = interp->int_max_str_digits;. Can we cast the PyConfig value to int32_t at this place, and use int in PyConfig?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mdickinson

mdickinson commented Sep 16, 2022

Copy link
Copy Markdown
Member

Using int32_t was a request from @mdickinson on the original PR.

Let's just leave it as is.

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mdickinson

mdickinson commented Sep 16, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Can we cast the PyConfig value to int32_t at this place

No, not safely, unless we know for sure that sizeof(int) == 4.

I'm more than happy to make sizeof(int) == 4 a CPython build-time assumption, since it's hardly ever likely not to be true, but in that case we should update the configure script to error out on systems where sizeof(int) is not 4, so that it's clear to all that it's safe to assume sizeof(int) == 4 within the CPython source.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

No, not safely, unless we know for sure that sizeof(int) == 4.

In PyConfig, we can reject values which don't fit in an uint32_t, to make sure that the cast is safe. No?

What's the issue on int max_str_digits = interp->int_max_str_digits; if interp->int_max_str_digits is an int32_t? When int is smaller than int32_t, or when int is larger than int32_t?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mdickinson

mdickinson commented Sep 16, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

In PyConfig, we can reject values which don't fit in an uint32_t, to make sure that the cast is safe. No?

Assuming you mean int32_t, not uint32_t, then yes: that would work fine.

What's the issue on int max_str_digits = interp->int_max_str_digits; if interp->int_max_str_digits is an int32_t?

The usual one whenever you're converting between two signed integer types that aren't known to have the same width - potential for undefined behaviour from overflow. There's no guarantee from the C standard that int has width exactly 32 bits. As I've already said, I have no objection to CPython assuming that the int type has width 32, but that assumption needs to be checked at build time and documented - if that's the way that people want to go, then that's fine with me.

I've put significant effort over the years into making sure that Object/longobject.c is highly portable and depends on very little beyond what's guaranteed by the C standards; I care very much about keeping that the case.

But really, this isn't worth the discussion at this point; I really wasn't anticipating that my request to use Py_ssize_t instead of int would cause this level of fuss. Let's just drop this change altogether and use int throughout, as in the current main branch.

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gpshead

gpshead commented Sep 16, 2022

Copy link
Copy Markdown
Member Author

Will do. Crafting the change was an informative exercise regardless. I'm a fan of always using explicitly sized types, so any attempt to do so in a codebase that didn't start out that way reveals how ready it is or isn't.

I'll dive back in on this code next week sometime, but questions in the back of my mind before I do that you may know already:

PyConfig vs PyInterpreterState vs subinterpreters? Right now I believe each subinterpreter gets its own limit. We need a test for that. I wouldn't want a sys.set_int_max_str_digits() call to change it for all interpreters if so.

Similarly, PyConfig... is that intended to be mutable beyond process startup? I thought might be meant to represent the environment and flag values at start, not not necessarily settings that were changed via APIs afterwards. thoughts?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

Mark:

There's no guarantee from the C standard that int has width exactly 32 bits. As I've already said, I have no objection to CPython assuming that the int type has width 32, but that assumption needs to be checked at build time and documented - if that's the way that people want to go, then that's fine with me.

I don't think that only this specific change is a good motivation to require 32-bit int to build Python.

So far, we did our best to support all architectures. We accepted a change to support m68k (the code was generalized recently: commit dec0757). Hum. I checked and it seems like m68k uses 32-bit int, at least on Linux ;-)

Well, it seems like on Linux, int is always 32-bit. I heard (Wikipedia) about ILP64 and SILP64 which use 64-bit int, but it seems to be outdated:

  • "ILP64: HAL Computer Systems port of Solaris to the SPARC64" but HAL Computer Systems defunct in 2001.
  • "SILP64: UNICOS" which was created in 1984; 38 years ago and is now discontinued

All modern operating systems use 32-bit int.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

Mark:

But really, this isn't worth the discussion at this point; I really wasn't anticipating that my request to use Py_ssize_t instead of int would cause this level of fuss. Let's just drop this change altogether and use int throughout, as in the current main branch.

My concern is more about the maintenance burden of Python/initconfig.c. Adding a new variable already requires a lot of code, as you can see in this PR. I would prefer to keep the code as simple as possible, by using only a few types: int, wchar_t* and list of wchar_t*. Supporting 3 types already requires different for each operation: cast to a dict, update from a dict, copy config to config2, print to stdout, etc.

I'm fine with using int32_t in longobject.c. IMO it's just a matter of making sure in PyConfig that the cast from PyConfig to uint32_t cannot overflow.

Gregory:

I'm a fan of always using explicitly sized types, so any attempt to do so in a codebase that didn't start out that way reveals how ready it is or isn't.

So maybe all PyConfig integer members should become int32_t? I don't know.

The int type comes from Python 3.6 global configuration variables like Py_VerboseFlag. In C, the int is commonly used and easy to use. For example, it's the "%i" formatter for printf(). For int32_t, the "%" PRIi32 formatter should be used instead and to be honest I had to use Google to find it since I only use it rarely.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 16, 2022

Copy link
Copy Markdown
Member

PyConfig vs PyInterpreterState vs subinterpreters? Right now I believe each subinterpreter gets its own limit.

Each interpreter has its own config in PyInterpreter.config. Technically, each interepreter can have a different config. It's already the case if you set config._isolated_interpreter to 1 to create an "isolated" sub-interpreter (enforce more restrictions on subprocesses and threads).

Oh, there was a test API to set _isolated_interpreter=1, but it seems like it's gone. Or I misremember.

Example modifying the sub-interpreter config with _testinternalcapi.set_config():

import _testcapi
import _testinternalcapi

code = """
import _testinternalcapi

config = _testinternalcapi.get_config()
config['bytes_warning'] = 2
_testinternalcapi.set_config(config)

print("bytes warnings of the sub-interpreter?",
      _testinternalcapi.get_config()['bytes_warning'])
"""
_testcapi.run_in_subinterp(code)

print("bytes warnings of main interpreter?",
      _testinternalcapi.get_config()['bytes_warning'])

Output:

bytes warnings of the sub-interpreter? 2
bytes warnings of main interpreter? 0

Modifying the sub-interpreter config doesn't modify the main interpreter config ;-)

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Sep 19, 2022
gh-96512: Update int_max_str docs to say 3.11 #96942
Merged
gh-96512: Move int_max_str_digits setting to PyConfig #96944
Merged
@gpshead

gpshead commented Sep 20, 2022

Copy link
Copy Markdown
Member Author

the pair of PRs mentioned above replace this with & address the feedback.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gpshead gpshead closed this Sep 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@vstinner vstinner vstinner requested changes
@mdickinson mdickinson Awaiting requested review from mdickinson

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@gpshead gpshead

Labels

awaiting changes DO-NOT-MERGE interpreter-core (Objects, Python, Grammar, and Parser dirs) tests Tests in the Lib/test dir type-feature A feature request or enhancement

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@gpshead @bedevere-bot @vstinner @mdickinson
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.