Tcl Source Code

View Ticket
Login
Ticket UUID: 3d978e0a84738b5c6a7bb1afc01aef962e9a25a4
Title: `clock -timezone` escapes safe interp via `~` path (8.6.16+, same gap as [2f8bb48009])
Type: Bug Created on: 2026-11-03 11:05:41
Submitter: bef Assigned to: jan.nijtmans
Subsystem: 16. Commands A-H Severity: Severe
Priority: 5 Medium Last modified: 2026-11-03 15:58:43
Status: Closed Closed by: jan.nijtmans
Resolution: Fixed Closed on: 2026-11-03 15:58:43
Version: 8.6.16, 8.6.17, 8.6.18 (core-8-6-branch as of 2026-11-03)
Description:

Follow-up to 2f8bb48009. That fix added ~ to msgcat's locale check, but clock's own guard in LoadTimeZoneFile (and LoadZoneinfoFile) still uses {^[/\\]|^[a-zA-Z]+:|(?:^|[/\\])\.\.}, which does not reject a leading ~. [clock] is aliased into safe interpreters and runs in the parent; on 8.6, file join $DataDir ~/x returns ~/x and source expands it. So a safe child can make the parent source any file below a home directory. No .msg suffix is appended and the name is not lowercased.

PoC (Tcl 8.6.18, core-8-6-branch head 52a3c1d31a):

file mkdir ~/tcldemo
set fd [open ~/tcldemo/tz w]
puts $fd {puts "PWNED issafe=[interp issafe] ns=[namespace current] exec=[info commands exec]"}
close $fd

catch {interp eval [interp create -safe] {clock format 0 -timezone :~/tcldemo/tz}}

file delete ~/tcldemo/tz

Output:

PWNED issafe=0 ns=::tcl::clock exec=exec

-timezone ~/tcldemo/tz (without colon), -t, and clock scan / clock add reach the same code. 9.0 and 9.1 are not affected (file join leaves ~ literal there).

This is a regression from 67e7522bd1 (fix for 2c237beffbace823, Etc/GMT±n), first released in 8.6.16. Before that, the guard was the allowlist ^[[.-.][:alpha:]_]+(?:/[[.-.][:alpha:]_]+)*$, which rejected ~. The clock-1.5.1-* tests cover .., //, zipfs: and C:, but not ~.

Fix: add ~ to both guards ({^[/\\~]|^[a-zA-Z]+:|(?:^|[/\\])\.\.}), or return to an allowlist that also admits digits and +, e.g. ^[A-Za-z0-9_+-]+(?:/[A-Za-z0-9_+-]+)*$. Sharing one helper with msgcat::UnsafeLocale would keep the two checks from drifting apart again. A safe-interp test along the lines of msgcat-1.15 (HOME set to a temp dir) would cover it.

User Comments:
serhiy.storchaka added on 2026-11-03 14:21:30:

Reproduced on core-8-6-branch and 8.6.16, not on 8.6.15.

Proposed fix in branch clock-unsafe-timezone-8-6: both guards also reject a leading "~". Only a leading "~" matters: on 8.6 a "~" later in the path stays inside the data directory.

9.0 and later are not affected, since "~" is not expanded there, although adding "~" to the guards there would be harmless.


jan.nijtmans added on 2026-11-03 15:58:43:

Fixed in [ee4f07c809|core-8-6-branch]

Many thanks!