Tcl Source Code

View Ticket
Login
Ticket UUID: 86b3c15f0c99a64f3aeb09eba009bccc1b726148
Title: ::unknown has infinite recursion in a corner case
Type: Patch Version: all
Submitter: kjnash Created on: 2024-02-02 19:14:38
Subsystem: 18. Commands M-Z Assigned To: jan.nijtmans
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2024-02-06 15:08:08
Resolution: Fixed Closed By: jan.nijtmans
    Closed on: 2024-02-06 15:08:08
Description:
The command ::unknown is careful to evaluate commands in the namespace of the caller - with one exception, which in a corner case can lead to infinite recursion.

The corner case is unusual but not perverse - it occurs when ::unknown is renamed into a different namespace.

Sometimes a package renames ::unknown - e.g. TclReadLine (a pure-Tcl replacement for GNU readline, https://wiki.tcl-lang.org/20215 ) renames ::unknown to ::_unknown and replaces it with its own ::unknown command, which calls ::_unknown.

While experimenting with improvements to TclReadLine, I decided to rename ::unknown into the ::TclReadLine namespace, in order to avoid pollution of the global namespace.  This exposes the bug. If the user types a command that exists in the ::TclReadLine namespace but not in the global namespace, the renamed ::unknown goes into infinite recursion: it believes it has found the command, but when it tries to execute it, it is not there.

A simple demonstration is to type these commands in an interactive shell:

  namespace eval ::foo {}
  rename unknown ::foo::unknown
  interp alias {} unknown {} ::foo::unknown
  proc ::foo::bar {} {}

  bar


The fix is simple, and is in the patch attached.  The bug is found in all versions of Tcl.

In view of the importance of ::unknown, I would appreciate review before committing this change.

diff -Naur original/library/init.tcl patched/library/init.tcl
--- original/library/init.tcl   2023-08-21 02:45:35.424433637 +0100
+++ patched/library/init.tcl    2024-02-02 18:41:18.049045928 +0000
@@ -371,7 +371,10 @@
            return -options $::tcl::UnknownOptions $::tcl::UnknownResult
        }
 
-       set ret [catch {set candidates [info commands $name*]} msg]
+       set ret [catch [list uplevel 1 [list info commands $name*]] msg]
+       if {$ret == 0} {
+           set candidates $msg
+       }
        if {$name eq "::"} {
            set name ""
        }
User Comments: jan.nijtmans added on 2024-02-06 15:08:08:

Fixed [524d68410a678b23|here].

Thanks for the report and the patch!


Attachments: