| Ticket UUID: | 526b01c0b9921b90618f82c398dc2684a60a053a | ||
| Title: | lset with 2+ indices on an unshared lseq list is a use-after-free (crash) | ||
| Type: | Bug | Created on: | 2026-10-18 09:48:35 |
| Submitter: | rocketship98 | Assigned to: | nobody |
| Subsystem: | 14. List Object | Severity: | Severe |
| Priority: | 7 High | Last modified: | 2026-10-30 09:23:40 |
| Status: | Closed | Closed by: | apnadkarni |
| Resolution: | Fixed | Closed on: | 2026-10-30 09:23:40 |
| Version: | 9.0.5, 9.1.0 | ||
| Description: | ||||
On an -O2 build the loop segfaults (TclThreadAllocObj, object pool corrupted).
On a Same with Cause: in TclLsetFlat, Found by mutation testing of the abstract-list code (using claude, so can't say i understand his reasoning). But it crashes my windows wish91.exe | ||||
| User Comments: | ||||
serhiy.storchaka added on 2026-10-18 10:19:50:
Confirmed on trunk and 9.0.5, and the analysis is right. Proposed fix in branch lset-abstract-list-uaf: TclLsetFlat() holds a reference to the sublist while storing it, the first option suggested here. Tests lset-19.1 and 19.2. juliannoble2 added on 2026-10-18 11:03:01:
Verified on one of APN's tclsfe9.1 builds and my own Tcl 9.0 builds (using non-standard zig build system)
Can crash even without the for loop in an interactive tclsh
set l [lseq 5]
lset l 1 0 X
or sometimes repeat the lset command.
apnadkarni added on 2026-10-30 04:23:30:
Reopening as I think the fix is both incorrect and incomplete. In particular, the lines
can land up calling TclListObjSetElement with a shared object which will cause a panic. Is there something that guarantees subListObj will only have a refcount of 0 before the IncrRefCount? However, an attempt to demonstrate is unexpected behavior:
The fact that the above caused a crash and not a panic leaves me wondering if this is one bug or two. serhiy.storchaka added on 2026-10-30 09:12:21:
TclListObjSetElement() panics only if the list it modifies (parentList) is shared, and it is unshared at this point. The element may be shared: TclListObjSetElement() takes its own reference, and the code right after the call makes a copy if subListObj became shared. The added Tcl_IncrRefCount()/Tcl_DecrRefCount() only keeps the old element alive while it is stored back, since it may be held only by the cache of the abstract list, which is freed during the call. I cannot reproduce the crash with the current main (1436d27450) or 9.0 (336b75d084), on Linux (also under valgrind) and on Windows 11 (MSVC):
% set m [lseq 5]
0 1 2 3 4
% lset m {2 0} X
0 1 X 3 4
What build and platform did you use?
apnadkarni added on 2026-10-30 09:23:15:
Sorry, mea culpa. Was testing a branch, not trunk, but the wrong branch, abstract-list-hook-fixes instead of lset-abstract-list-uaf. Confused between the various lset/abstract list tickets and branches. Apologies for the waste of time. apnadkarni added on 2026-10-30 09:23:40:
Changed back to closed. | ||||
