gh-143050: add helper _PyLong_InitTag()#147956
gh-143050: add helper _PyLong_InitTag()#147956skirpichev wants to merge 6 commits intopython:mainfrom
Conversation
With this we can assume, that _PyLong_Set*() operate on non-immortal integers.
Include/internal/pycore_long.h
Outdated
| { | ||
| assert(PyLong_Check(op)); | ||
| op->long_value.lv_tag = 1; /* non-immortal zero */ | ||
| op->long_value.ob_digit[0] = 0; |
There was a problem hiding this comment.
I would prefer to only initialize the tag and so rename the function to _PyLong_InitTag().
op->long_value.ob_digit[0] = 0; is only used (needed) by long_alloc(). All other functions using _PyLong_Init() will rewrite ob_digit[0] immediately and so it's inefficient.
There was a problem hiding this comment.
I think this may trigger a compiler warning. I did renaming, lets do another change in a second commit.
There was a problem hiding this comment.
lets do another change in a second commit.
You didn't make the second commit yet.
There was a problem hiding this comment.
As I expected, that's relevant:
https://github.com/python/cpython/actions/runs/23846329148/job/69514515704
| _PyLong_SetSignAndDigitCount(result, size != 0, size); | ||
| /* The digit has to be initialized explicitly to avoid | ||
| * use-of-uninitialized-value. */ | ||
| result->long_value.ob_digit[0] = 0; |
There was a problem hiding this comment.
While I'm not convinced that this initialization is useful, the comment says that it is. We should keep it, no?
There was a problem hiding this comment.
I suggested to keep this initialization. You marked my comment as resolved, I don't understand. Do you consider that it's not needed?
An alternative would be to fill digits with a pattern to detect usage of non-initialized memory when Python is built in debug mode:
#ifdef Py_DEBUG
// Fill digits with a known pattern to detect usage of uninitialized memory
memset(result->long_value.ob_digit, 0xff,
sizeof(result->long_value.ob_digit[0]) * ndigits);
#endifWe use this strategy for:
str: seeunicode_fill_invalid()function.PyBytesWriter:memset(byteswriter_data(writer), 0xff, byteswriter_allocated(writer))inPyBytesWriter_Create()
There was a problem hiding this comment.
Do you suggest to keep this just here? BTW, tests pass - I rerun one job again.
|
"Tests / Android (x86_64)" failed with: |
|
This yes, but linked above fuzzer failure is relevant.
…On Wed, Apr 1, 2026, 15:10 Victor Stinner ***@***.***> wrote:
*vstinner* left a comment (python/cpython#147956)
<#147956?email_source=notifications&email_token=AAQOKGA5T2QHBNZ3SVWPVWT4TUBM7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJWHE3DGNZXGIZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2LK4DSL5RW63LNMVXHIX3POBSW4X3DNRUWG2Y#issuecomment-4169637722>
"Tests / Android (x86_64)" failed with: Error on ZipFile unknown archive.
It's unrelated to this change.
> Task :app:maxVersionSetup
"Install Android Emulator v.36.4.10" ready.
Installing Android Emulator in /usr/local/lib/android/sdk/emulator
"Install Android Emulator v.36.4.10" complete.
"Install Android Emulator v.36.4.10" finished.
Checking the license for package AOSP ATD Intel x86_64 Atom System Image in /usr/local/lib/android/sdk/licenses
License for package AOSP ATD Intel x86_64 Atom System Image accepted.
Preparing "Install AOSP ATD Intel x86_64 Atom System Image API 32 (revision 1)".
Warning: An error occurred while preparing SDK package AOSP ATD Intel x86_64 Atom System Image: Error on ZipFile unknown archive.:
java.io.IOException: Error on ZipFile unknown archive
at org.apache.commons.compress.archivers.zip.ZipFile.<init>(ZipFile.java:383)
at org.apache.commons.compress.archivers.zip.ZipFile.<init>(ZipFile.java:323)
at org.apache.commons.compress.archivers.zip.ZipFile.<init>(ZipFile.java:279)
at com.android.repository.util.InstallerUtil.unzip(InstallerUtil.java:101)
at com.android.repository.impl.installer.BasicInstaller.doPrepare(BasicInstaller.java:91)
—
Reply to this email directly, view it on GitHub
<#147956?email_source=notifications&email_token=AAQOKGA5T2QHBNZ3SVWPVWT4TUBM7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJWHE3DGNZXGIZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2LK4DSL5RW63LNMVXHIX3POBSW4X3DNRUWG2Y#issuecomment-4169637722>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAQOKGB4LCCKKOKT66AWXQD4TUBM7AVCNFSM6AAAAACXIS7L4WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DCNRZGYZTONZSGI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
"Tests / CIFuzz / cpython3 (memory)" CI logs an pycore_long.h:243 is the following assertion in assert((_PyLong_IsCompact(op)
&& _PY_IS_SMALL_INT(_PyLong_CompactValue(op)))
|| (!is_small_int));Oh right, |
vstinner
left a comment
There was a problem hiding this comment.
What do you think of changing _PyLong_IsSmallInt() implementation like that?
static inline bool
_PyLong_IsSmallInt(const PyLongObject *op)
{
assert(PyLong_Check(op));
bool is_small_int = (op->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
if (is_small_int) {
assert(PyLong_CheckExact(op));
assert(_Py_IsImmortal(op));
assert((_PyLong_IsCompact(op)
&& _PY_IS_SMALL_INT(_PyLong_CompactValue(op))));
}
return is_small_int;
}The current code runs many checks when is_small_int is false which are not needed.
Lets try, but I suspect it might fall in other place. |
| /* Initialize a freshly-allocated int. | ||
| * | ||
| * Fast operations for single digit integers (including zero) | ||
| * assume that there is always at least one digit present. | ||
| * The digit has to be initialized explicitly to avoid | ||
| * use-of-uninitialized-value. | ||
| */ |
There was a problem hiding this comment.
This function doesn't initialize the first digit, so I'm not sure why you reintroduce the "The digit has to be initialized explicitly" comment:
| /* Initialize a freshly-allocated int. | |
| * | |
| * Fast operations for single digit integers (including zero) | |
| * assume that there is always at least one digit present. | |
| * The digit has to be initialized explicitly to avoid | |
| * use-of-uninitialized-value. | |
| */ | |
| /* Initialize the tag of a freshly-allocated int. */ |
| _PyLong_SetSignAndDigitCount(result, size != 0, size); | ||
| /* The digit has to be initialized explicitly to avoid | ||
| * use-of-uninitialized-value. */ | ||
| result->long_value.ob_digit[0] = 0; |
There was a problem hiding this comment.
I suggested to keep this initialization. You marked my comment as resolved, I don't understand. Do you consider that it's not needed?
An alternative would be to fill digits with a pattern to detect usage of non-initialized memory when Python is built in debug mode:
#ifdef Py_DEBUG
// Fill digits with a known pattern to detect usage of uninitialized memory
memset(result->long_value.ob_digit, 0xff,
sizeof(result->long_value.ob_digit[0]) * ndigits);
#endifWe use this strategy for:
str: seeunicode_fill_invalid()function.PyBytesWriter:memset(byteswriter_data(writer), 0xff, byteswriter_allocated(writer))inPyBytesWriter_Create()
With this we can assume, that _PyLong_Set*() operate on non-immortal integers.