|
2026-03-13
| ||
| 08:11 | • Ticket [f7495f63c0] valgrind reports uninitialized memory access running the tes... artifact: f5553a9eef user: jan.nijtmans | |
| 08:00 | Merge 9.0 - Revert commit [f752c1271a]. See ticket [f7495f63c0] check-in: 24012d5118 user: apnadkarni tags: trunk, main | |
| 03:20 | Revert commit [f752c1271a]. See ticket [f7495f63c0] check-in: dfd8f12ec5 user: apnadkarni tags: core-9-0-branch | |
| 03:17 | • Ticket [f7495f63c0] valgrind reports uninitialized memory access running the tes... artifact: be27c75d09 user: apnadkarni | |
|
2026-03-12
| ||
| 13:13 | • Ticket [f7495f63c0]: 5 changes artifact: f70b70c594 user: sebres | |
| 10:42 | • Ticket [f7495f63c0]: 5 changes artifact: c630e81d36 user: jan.nijtmans | |
| 10:28 | • Ticket [f7495f63c0]: 5 changes artifact: 9b435e683e user: sebres | |
| 07:57 | • Ticket [f7495f63c0]: 5 changes artifact: 5128228f8e user: jan.nijtmans | |
|
2026-03-11
| ||
| 20:26 | • Ticket [f7495f63c0]: 5 changes artifact: 2503991997 user: sebres | |
| 16:37 | • Ticket [f7495f63c0]: 5 changes artifact: 039c0b5142 user: jan.nijtmans | |
| 16:33 | • Ticket [f7495f63c0]: 5 changes artifact: 1f4f953377 user: sebres | |
| 15:58 | • Ticket [f7495f63c0]: 5 changes artifact: 1edc08a736 user: jan.nijtmans | |
| 15:40 | • Ticket [f7495f63c0]: 5 changes artifact: d6a0f70292 user: sebres | |
| 10:45 | • Ticket [f7495f63c0]: 5 changes artifact: 61ced01f29 user: jan.nijtmans | |
| 02:53 | • Ticket [f7495f63c0]: 5 changes artifact: 049f5e5503 user: apnadkarni | |
|
2026-03-10
| ||
| 18:35 | • Ticket [f7495f63c0]: 5 changes artifact: ba7ef13a54 user: jan.nijtmans | |
| 17:41 | • Ticket [f7495f63c0]: 5 changes artifact: 18d0f8c023 user: apnadkarni | |
| 14:48 | Revert 'type' field back to an int. In stead fix the Tcl_InitHashTable() call. See [f7495f63c0] check-in: f752c1271a user: jan.nijtmans tags: core-9-0-branch | |
|
2026-03-09
| ||
| 07:11 | • Ticket [f7495f63c0] valgrind reports uninitialized memory access running the tes... artifact: dc539e183e user: jan.nijtmans | |
| 07:09 | Fix [f7495f63c0]. Valgrind reported error in SetScriptLimitCallback check-in: 1b6b19bd55 user: jan.nijtmans tags: core-8-6-branch | |
|
2026-03-08
| ||
| 04:07 | • Closed ticket [f7495f63c0]: valgrind reports uninitialized memory access running... artifact: 1f7802c6b6 user: apnadkarni | |
| 04:04 | Merge 9.0. Fix [f7495f63c0]. Valgrind reported error in SetScriptLimitCallback check-in: 9f41398a11 user: apnadkarni tags: trunk, main | |
| 03:57 | Fix [f7495f63c0]. Valgrind reported error in SetScriptLimitCallback check-in: 58acf5656f user: apnadkarni tags: core-9-0-branch | |
|
2026-03-06
| ||
| 11:22 | • Ticket [f7495f63c0] valgrind reports uninitialized memory access running the tes... artifact: 8c4bd64611 user: apnadkarni | |
| 11:07 | Fix [f7495f63c0]. Valgrind reported error in SetScriptLimitCallback closed check-in: edef37bbf6 user: apnadkarni tags: bug-f7495f63c0 | |
| 10:47 | • New ticket [f7495f63c0] valgrind reports uninitialized memory access running the... artifact: 5c7a4d5478 user: apnadkarni | |
| Ticket UUID: | f7495f63c01ea80069198ac183e1e0cc8873dabe | ||
| Title: | valgrind reports uninitialized memory access running the test suite | ||
| Type: | Bug | Created on: | 2026-03-06 10:47:53 |
| Submitter: | apnadkarni | Assigned to: | apnadkarni |
| Subsystem: | - New Builtin Commands | Severity: | Severe |
| Priority: | 5 Medium | Last modified: | 2026-03-13 08:11:02 |
| Status: | Closed | Closed by: | jan.nijtmans |
| Resolution: | Fixed | Closed on: | 2026-03-13 08:11:02 |
| Version: | trunk | ||
| Description: | ||||
==269512== Use of uninitialised value of size 8 ==269512== at 0x497CFC8: Tcl_DeleteHashEntry (tclHash.c:414) ==269512== by 0x49891F0: DeleteScriptLimitCallback (tclInterp.c:4185) ==269512== by 0x4988D95: TclLimitRemoveAllHandlers (tclInterp.c:3790) ==269512== by 0x48802A9: DeleteInterpProc (tclBasic.c:2029) ==269512== by 0x49DC268: Tcl_EventuallyFree (tclPreserve.c:296) ==269512== by 0x48800B7: Tcl_DeleteInterp (tclBasic.c:1929) ==269512== by 0x498768E: ChildObjCmdDeleteProc (tclInterp.c:2743) ==269512== by 0x4882C80: Tcl_DeleteCommandFromToken (tclBasic.c:3858) ==269512== by 0x4983D35: NRInterpCmd (tclInterp.c:891) ==269512== by 0x4883E8D: Dispatch (tclBasic.c:4732) ==269512== by 0x4883EF1: TclNRRunCallbacks (tclBasic.c:4748) ==269512== by 0x4883799: Tcl_EvalObjv (tclBasic.c:4486) ==269512== by 0x4885F5D: TclEvalEx (tclBasic.c:5587) ==269512== by 0x49AA7F7: Tcl_FSEvalFileEx (tclIOUtil.c:1793) ==269512== by 0x49BC5AA: Tcl_MainEx (tclMain.c:408) ==269512== by 0x400701: main (tclAppInit.c:98) | ||||
| User Comments: | ||||
apnadkarni added on 2026-03-06 11:22:03:
Also present in core-9-0-branch since [24d8933cbd]. Proposed fix at [edef37bbf6]. The change of the ScriptLimitCallbackKey.type field from long to int introduced trailing pad bytes in a structure used as a key in hash tables. Uninitialized pad bytes are a no-no for structs used as hash keys. Now changed to be intptr_t which should not result in pad bytes as it will always be same size as a pointer on all known platforms. apnadkarni added on 2026-03-08 04:07:15:
Fixed in [58acf5656f]. jan.nijtmans added on 2026-03-09 07:11:51:
Good catch! Solution works. However, if I may .....(Sorry!). Two remarks: 1) In Tcl 8.6, the "type" field still is a long, which is 32-bits on Windows 64. So, the same problem is there too. 2) I don't think the presence of the pad bytes is the problem. The hashtable initialization is! If you calculate the size as "sizeof(ScriptLimitCallbackKey)/sizeof(int))", the pad-bytes are counted as well, that's the real problem. Fixed that [1b6b19bd5561efc0|here], for 8.6 too. Are you OK with forwarding this solution to 9.0 and 9.1 too? apnadkarni added on 2026-03-10 17:41:04:
Sorry, I did not see your response earlier. It appears I am not getting mail notifications from bugs any more and I'm currently only sporadically tracking the tickets. Yep, I know Windows also had the problem. But it was visible on Unix thanks to valgrind. I think if there were tests that forced collisions in the hash table, it might have shown up on both platforms. I am uncomfortable with your solution for two reasons. First, the C standard does not guarantee the 4 pad bytes appear as trailing bytes. They could appear before the type field (and no trailing pad) while still maintaining the alignment and field ordering guarantees made by the standard. And in that case your code would break. Having said that, perhaps I'm being pedantic and no compiler does that in the real world. Second, and more important, consider that with your code, the
casts the 12 byte allocation to a pointer to a 16 byte struct! I really do not like this kind of violation of compiler contracts no matter whether there is any practical effect. So I would still prefer the fix I had proposed particularly as I don't see any particular advantage of your solution. jan.nijtmans added on 2026-03-10 18:35:10:
> I don't see any particular advantage of your solution The advantage of my solution is that the type field can be accessed with a 32-bit access, not with a 64-bit access, and all hash-key compares only have to compare 3 integers not 4! That's surely better. Hoping you see it now. > First, the C standard does not guarantee the 4 pad bytes appear as trailing bytes. Hmmm. You are wrong here. Padding always concerns trailing bytes! > I really do not like this kind of violation of compiler contracts no matter whether there is any practical effect. Sorry, wrong again. On a 64-bit platform, which requires 64-bit values to be properly aligned, all mallocs are aligned on 8-byte boundaries. So, please don't invent problems which don't exist. apnadkarni added on 2026-03-11 02:53:59:
Read the standard. There is no such requirement anywhere. The only thing the standard mandates is maintaining the order of fields in a struct and alignment of individual fields. The location and length of pad bytes is completely up to the compiler except that the padding cannot be at the beginning of the struct. But, as I said I'm being pedantic and may not matter in practice.
Your reference to malloc indicates you simply did not understand the issue. It has nothing to do with allocation strategy. It's a compiler expectation that (ScriptLimitCallbackKey *) will point to a location with sizeof(ScriptLimitCallbackKey) valid bytes, no matter how they were allocated, dynamically via malloc, on the stack, or static storage. What your code is doing is the equivalent of this:
And with regards to accessing 32-bits versus 64-bits, cost of memory and registry operations is still the same on 64-bit platforms. And if you think adding one less integer is an "optimization" for Tcl performance, I don't know what to say! In any case, do as you wish. I'm not going to comment further. jan.nijtmans added on 2026-03-11 10:45:12:
Noted! Thanks! sebres added on 2026-03-11 15:40:45:
> It has nothing to do with allocation strategy. I'm agree. However it is indirectly about allocation, see below... > It's a compiler expectation that (ScriptLimitCallbackKey *) will point to a location with sizeof(ScriptLimitCallbackKey) valid bytes Nope. The compiler expectation here is to allocate, compare, hash, whatever action with an array of int's, and although it allows so to handle structures in a hash-table, it doesn't do that using structures internally, because the hash-table API considers blocks of int's, nothing else. So if you want to estimate the impact of the change of Jan, you shall rather consider it as array of ints everywhere. But!.. The fix of Jan may indeed introduce a problem indirectly, since AllocArrayEntry could allocate a block for hash-entry + 3 integers (12 bytes), it can indeed cause many issues at corner case (e. g. ckalloc returns last block matching the range of buffer). And in best case an UB, since casting a struct that requires 16 bytes (due to 4 bytes of trailing padding) to a memory block of only 12 bytes may lead to UB by an attempt to access the struct as a whole or to use it in certain operations. So I am with Ashok here and it shall be rewritten (at least for 64-bit target). jan.nijtmans added on 2026-03-11 15:58:45:
Wasting too much time in this kind of discussion, but .... > ... may lead to UB by an attempt to access the struct as a whole or to use it in certain operations The struct is _never_ accessed as a whole. Inside the Tcl_HashTable it is always accessed as 3 integers. Outside the Tcl_HashTables, there is no way to access the padding storage in the struct. If there's something wrong, valgrind should be able to show that. Sorry! sebres added on 2026-03-11 16:33:13:
> Outside the Tcl_HashTables, there is no way to access the padding storage in the struct. But there is it, right now, see for instance TclRemoveScriptLimitCallbacks. Don't forget that optimization is compiler related thing and... Just imagine it'd write a byte code using 64-bit instruction to load Also it may do that later by some changes if we'd get more of such casts. Much worse if it'd be the write case. I know that it is broken by design, but lets not make it more faulty as it is. jan.nijtmans added on 2026-03-11 16:37:29:
> But there is it, right now, see for instance TclRemoveScriptLimitCallbacks. I see keyPtr->interp and keyPtr->type being accessed, that's not the structure 'as a whole'. sebres added on 2026-03-11 20:26:26:
Are you in mode to cherry-pick matching words, Jan?.. Also you seemed to miss "or to use it in certain operations" after "access the struct as a whole". Anyway, because it is valid to optimize the code in the way I described above (if the compiler knows that after keyPtr->type there are still 4 bytes for padding), I can not exclude that by code like this (e. g. using ARM instructions like LDR, LDM or LDP) it would not cause SF by out-of-bounds memory access:
Can you really swear that it won't happen? jan.nijtmans added on 2026-03-12 07:57:42:
Well, just for the fun, I checked the assembler generated (both with gcc and clang on ARM64, maximum optimization -O3). It looked like:
mov x1, x0
ldr x0, [x0]
ldr w1, [x1, 8]
b Tcl_LimitRemoveHandler(void*, int)
Conclusion: the optimization you suggest, which I don't think is valid, isn't done by either gcc or clang. If - for the fun - the type of "type" is changed to ptrdiff, I get:
ldp x0, x1, [x0]
b Tcl_LimitRemoveHandler(void*, long)
so the compiler knows the ldp instruction.
I did another experiment, forcing 64-bit access in this exact place. The result was a crash in testcase interp-34.7. So whenever a compiler does this (in my view) invalid optimization, we have a testcase for it, which triggers consistently. I'm sure that, whenever a compiler introduces this optimization, Tcl will not be the only project broken. (It would not be the first time this happens) sebres added on 2026-03-12 10:28:26:
OK, now I'm agree with Ashok - you don't understand what the issue is. If today the compiler doesn't optimize it using LDP instruction yet, it can do that later, because this will be not "broken" as you said, but fully legitimate thing - the compiler knows that the structure has a padding (basically it is 16 bytes large), so LDP instruction would be fully correct here. It is strange to push this on the fact that it is not currently in use. Maybe simply not yet? Or not in this optimization level, but within -O3 etc. Or simply by certain circumstances... But what it surely unable to know, is that we had casted it to a 12-byte block of memory (since this allocation happens in another files). This is definitively an UB. Even a known and still working UB remains UB, and it is weird to listen you decided to retain it... Not to mention you're trying to rely to some test coverage. Test coverage of the UB?! Really? This is calling bomb-planting, Jan... And saying that I get dejavu feeling. jan.nijtmans added on 2026-03-12 10:42:28:
So, let's agree we disagree then. You think this would be a valid (future) optimization. I think it won't be a valid optimization at all, since it would do read access of uninitialized padding bytes. I wasted enough time on this discussion already. sebres added on 2026-03-12 13:13:23:
It is not about "think", it is about "know". Because I know this. The compiler is entitled to assume that an object of type struct occupies its full
Jan, this sentence doesn't make sense at all.
Why shall the padding be initialized? And what do you think the padding is for?
The alignment (for faster CPU access) is only one purpose (and almost of little importance)...
Apropos alignment, who guarantees that the block retrieved by Again, you disagree not with me, but with C-standard. Or rather with the fact that such cast can be considered as an UB. Particularly with this 3 paragraphs in the C-standard:
Some UBs may works years till some days they'd stop to work. apnadkarni added on 2026-03-13 03:17:42:
Jan, Sorry, I'm reverting your revert.
Finally, even if you don't change your opinion, please consider that in case of disagreements, it's reasonable that the majority opinion hold. With apologies, /Ashok jan.nijtmans added on 2026-03-13 08:11:02:
apologies accepted :-) The rule "better safe than sorry" applies here too. The performance difference between the two solutions is negligible anyway, it's not worth further discussion. I learned a lot (I never heard of the the ARM ldp instruction ...) | ||||
