#3170 possible to have an empty info nameofexecutable

obsolete: 8.4.19
open
Don Porter
5
2011-10-13
2005-06-21
David N. Welton
No

How to reproduce:

1) Make a local copy of tclsh called something
*besides* tclsh or anything else that is in your path.
Make sure that '.' is not in your path.

2) Compile this, using the right paths and name of the
executable for your environment:

#include <unistd.h>

int main(int argc, char *argv[], char *envp[]) {
char *myargv[] = {"mytclsh", NULL};
execve("/home/davidw/workshop/protect/mytclsh",
myargv, envp);
}

3) Run it, try 'info name' - it's empty.

Here's why:

5454 execve("/home/davidw/workshop/protect/mytclsh",
["mytclsh"], [/* 29 vars *
/]) = 0

argv0 has no / in it, and mytclsh is not in PATH, so we
hit tclUnixFile.c line 123, and thus have no info
nameofexecutable.

I found this bug while trying to get starpacks working
as CGI's under Apache. The execve program above is a
recreation of how Apache launches CGI's.

Thanks,
Dave

Discussion

1 2 > >> (Page 1 of 2)
  • Logged In: YES
    user_id=240

    Quick patch inline:

    This seems to fix the problem, but I don't know if it causes
    others.

    --- tclUnixFile.c 1 Dec 2004 23:18:55 -0000 1.44
    +++ tclUnixFile.c 21 Jun 2005 14:42:29 -0000
    @@ -120,8 +120,12 @@
    p++;
    }
    }
    - TclSetObjNameOfExecutable(Tcl_NewObj(), NULL);
    - goto done;
    + /* It's not in the path, so just use it in the current
    working
    + * directory. */
    + Tcl_DStringSetLength(&buffer, 0);
    + name = Tcl_DStringAppend(&buffer, "./", -1);
    + name = Tcl_DStringAppend(&buffer, argv0, -1);
    + goto gotName;

     
  • Don Porter
    Don Porter
    2005-06-21

    Logged In: YES
    user_id=80530

    hmmm....

    the proposed patch takes effect
    only after the PATH has been
    searched.

    So whenever a program by the
    same truncated name *is* on
    the PATH, Tcl will find the wrong one.

    Seems that Apache really ought to
    pass the absolute name in argv0.

     
  • Logged In: YES
    user_id=1983

    Great catch!

    Possible solutions: on Linux and systems which support /proc, you could get at
    the exe's own path via "/proc/self/exe" which is a symlink pointing to the right file.

    Another way out might be http://autopackage.org/docs/binreloc/

    As workaround, I s'pose one could use full pathnames in apache?

    -jcw

     
  • Don Porter
    Don Porter
    2005-06-21

    Logged In: YES
    user_id=80530

    and on the other side of things,
    TclpFindExecutable() comments
    say [info nameof] will be set to NULL
    if the executable can't be found.

    Won't this patch prevent that by
    always supplying ./foo as a
    (possibly false) last resort ?

     
  • Logged In: YES
    user_id=121110

    While this bug appears to be a Unix only, the correct
    behavior needs to be verified on Windows and, if applicable,
    MacOsX.

     
  • Logged In: YES
    user_id=240

    The patch was a quick hack and the objections to it are
    quite valid... I'm sure we can come up with something better.

    However, even if Apache isn't doing things quite right, I
    don't think it's egregiously wrong, either, and I think that
    Tcl ought to be "liberal with what it accepts" as a general
    principle.

    How does this sound for an algorithm:

    1) If argv0 has slashes in it, we are ok, we just use that.

    2) If there are no slashes, and a file that matches
    pwd+argv0 exists and is executable, use that.

    3) Search the PATH.

    4) Can't figure anything out, so just make it NULL.

     
  • Don Porter
    Don Porter
    2005-06-21

    Logged In: YES
    user_id=80530

    the thing I dislike about that
    is its implicit assumption that
    "." is first on the PATH.

    For executables actually launched
    from the shell (still the vast majority,
    right?), that can give false answers.

    If we have no choice but to give
    a false answer to one or the other,
    I'd give it to Apache.

     
  • Pat Thoyts
    Pat Thoyts
    2005-06-21

    Logged In: YES
    user_id=202636

    To reply to gwlester's point - this isn't really an issue on
    Windows as on windows there is a reliable way to find the
    path to the binary using GetModuleFilename() and this is
    used in win/tclAppInit.c to set argv[0] appropriately.

     
  • Logged In: YES
    user_id=79902

    I'd be tempted to put the search of the PATH first too.

    OTOH, doing a readlink("/proc/self/exe") would be better on
    those OSes that support it (alas, neither Solaris or Irix as
    far as I can see) though if we do that, it perhaps ought to
    be a feature searched for using configure? (We'd need a
    fallback anyway in case /proc isn't mounted for some odd
    reason.)

     
  • Logged In: YES
    user_id=79902

    FreeBSD (of uncertain vintage but probably fairly old)
    vintage supports the following readlink() target:
    /proc/[pid]/file
    (no /proc/self, different name inside)

     
  • Logged In: YES
    user_id=90580

    Mac OS X 10.2 and later has
    #include <mach-o/dyld.h>
    int _NSGetExecutablePath(char *buf, unsigned long *bufsize);

     
  • Logged In: YES
    user_id=240

    The apache folks seem to be of the opinion that that system
    behaves like it does as historical accident. However, they
    don't really regard it as a bug, and aren't about to change
    it (who knows who may depend on it at this point), so it
    doesn't look as if we can shuffle this problem off to them.

     
  • Roy Keene
    Roy Keene
    2009-03-20

    This is still a problem for me. I would like TclpFindExecutable() to be more robust and look for OS-specific ways to determine the executable name.

    Something similar to:

    const char *exepaths[] = { "/proc/self/exe", "/proc/%lu/exe", "/proc/%lu/file", "/proc/%lu/object/a.out" };
    char pathbuf[PATH_MAX + 1], pathbuf_copy[PATH_MAX + 1];
    int pathbuf_len;
    int exefileexists, recursedepth, idx;

    if (name == NULL || *name == '\0') {
    /* Attempt to use "/proc/self/exe" to determine where we are */
    for (idx = 0; idx < (sizeof(exepaths) / sizeof(*exepaths)); idx++) {
    if (strchr(exepaths[idx], '%') == NULL) {
    sprintf(pathbuf_copy, exepaths[idx]);
    } else {
    sprintf(pathbuf_copy, exepaths[idx], (unsigned long) getpid());
    }

    exefileexists = access(pathbuf_copy, F_OK);

    if (exefileexists == 0) {
    for (recursedepth = 0; recursedepth < 100; recursedepth++) {
    pathbuf_len = readlink(pathbuf_copy, pathbuf, sizeof(pathbuf));
    if (pathbuf_len < 0) {
    break;
    }
    if (pathbuf_len >= sizeof(pathbuf) || pathbuf_len == 0) {
    return;
    }

    memcpy(pathbuf_copy, pathbuf, pathbuf_len);
    pathbuf_copy[pathbuf_len] = '\0';
    }

    name = pathbuf_copy;
    goto gotName;
    }
    }
    }

    in unix/tclUnixFile.c's TclpFindExecutable if it can't find the executable using the current methods.

     
  • Steven
    Steven
    2011-10-11

    Hmmm... Scid vs. PC now suffers from this problem with wish8.6b2. We compile our own custom wish - tkscid - and install it into local/bin (or bin - they both bug out), and from it [info nameofexecutable] just returns {}. Never seen this bug before 8.6b2, and not sure what to do.

     
  • Doing a readlink() on /proc/[pid]/exe is elegant, but won't work on OSX. There, the only reliable method I've found for getting the executable path from the PID is by calling lsof in a subprocess:

    lsof -a -p [pid] -F n -d txt

    Then you need the first line that starts with 'n', and you need all of that line except the first character. Ugh. Works though. (Poking in the normal system process information doesn't work; that gives you the process name without the path to it.)

     
  • Steven
    Steven
    2011-10-12

    > Doing a readlink() on /proc/[pid]/exe is elegant

    This should come in handy. Cheers

    Just clarifying - i never had this issue with any version of 8.4, 8.5 or 8.6a1, so perhaps it is a regression.

     
  • I also like the dladdr() solution, though the name it yields is not necessarily normalized. Still, that's a practical solution (and would be easy to check for with autoconf).

    My little test code:

    #include <dlfcn.h>
    #include <stdio.h>
    int main(int argc,char**argv) {
    Dl_info info;
    if (!dladdr(main,&info)) {
    printf("not found\n");
    return 1;
    }
    printf("\"%s\"\n",info.dli_fname);
    return 0;
    }

     
  • dgp: What's the position on normalization of [info nameofexecutable]?

    (dladdr() is present on OSX, Solaris and Linux for sure: I've checked.)

     
    • milestone: --> 2051021
     
  • I've committed something on the bug-1224888 branch. It needs more work (especially detection of the validity of the magic!) but it would be interesting to see if it works.

    https://core.tcl.tk/tcl/timeline?r=bug-1224888

     
    • milestone: 2051021 --> obsolete: 8.4.19
     
  • Don Porter
    Don Porter
    2011-10-13

    The branch produces segfault in test unixFile-1.1

    This is on a platform where..

    checking for dladdr... no

     
  • Don Porter
    Don Porter
    2011-10-13

    Program received signal SIGSEGV, Segmentation fault.
    0x0000003716e7288e in free () from /lib64/libc.so.6
    (gdb) bt
    #0 0x0000003716e7288e in free () from /lib64/libc.so.6
    #1 0x00000000004cb7d2 in TclpFree (
    cp=0x2f6c74692f3a6e69 <Address 0x2f6c74692f3a6e69 out of bounds>)
    at /home/dgp/fossil/tcl8.4/unix/../generic/tclAlloc.c:710
    #2 0x0000000000429ea5 in Tcl_Free (
    ptr=0x2f6c74692f3a6e69 <Address 0x2f6c74692f3a6e69 out of bounds>)
    at /home/dgp/fossil/tcl8.4/unix/../generic/tclCkalloc.c:1167
    #3 0x00000000004a86d6 in Tcl_DStringFree (dsPtr=0x7fffffff7f10)
    at /home/dgp/fossil/tcl8.4/unix/../generic/tclUtil.c:1698
    #4 0x00000000004b6788 in TclpFindExecutable (argv0=0x783ca0 "junk")
    at /home/dgp/fossil/tcl8.4/unix/tclUnixFile.c:225
    #5 0x0000000000454329 in Tcl_FindExecutable (argv0=0x783ca0 "junk")
    at /home/dgp/fossil/tcl8.4/unix/../generic/tclEncoding.c:1228
    #6 0x0000000000420239 in TestfindexecutableCmd (clientData=0x0,
    interp=0x700030, argc=2, argv=0x7fffffff8320)
    at /home/dgp/fossil/tcl8.4/unix/tclUnixTest.c:465

     
  • Thank you for testing; I'd put the code two lines too high. :-)

     
1 2 > >> (Page 1 of 2)