Thread overview
[Issue 21656] [REG2.091] Wrong file read during exception stringification leads to SIGBUS
Feb 22
kinke
Feb 23
kinke
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

kinke <kinke@gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |kinke@gmx.net

--- Comment #1 from kinke <kinke@gmx.net> ---
This seems to boil down to the usage of `program_invocation_name` on Linux to obtain the name of the executable file (though that hasn't changed in the linked druntime PR). It's apparently just `argv[0]` and so not really robust. Not sure if BSD's `getprogname()` is better in this regard. Too bad `std.file.thisExePath()` is in Phobos and thus not usable.

--
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #2 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to kinke from comment #1)
>  (though that hasn't changed in the linked druntime PR)

But, why would bisection point to that PR as the earliest point for the observed SIGBUS, then?

> Too bad `std.file.thisExePath()` is in Phobos and thus not usable.

I don't see why it couldn't be moved to Druntime. It looks like it would be a good fit.

--
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #3 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
BTW, not sure about other systems, but on Linux, reading /proc/self/exe is going to be more reliable than via thisExePath. The reason being that /proc/self/exe is a special directory entry which continues to be valid (openable) even when the target that it (as a symlink) is pointing at is gone. So, perhaps just moving thisExePath to Druntime is not the best option.

--
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

ZombineDev <petar.p.kirov@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |petar.p.kirov@gmail.com

--- Comment #4 from ZombineDev <petar.p.kirov@gmail.com> ---
Could we move it to druntime and then make it use `/proc/self/exe` on Linux?

--
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #5 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
It would have to be a different function. `thisExePath` returns a string, but what we really need is a file descriptor. (And, we can't return the string literal "/proc/self/exe" from `thisExePath` on Linux, that would break other uses such as reading files adjacent to the program's executable.)

--
February 22
https://issues.dlang.org/show_bug.cgi?id=21656

Jacob Carlborg <doob@me.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |doob@me.com

--- Comment #6 from Jacob Carlborg <doob@me.com> ---
`thisExePath` uses `/proc/self/exe` on Linux, but it uses `readlink` to read out the path, instead of opening the actual file.

`getprogname` is no better than `program_invocation_name`. They have more or less the same semantics. `getprogname` returns what was stored by `setprogname`. This is what the man pages say about `setprogname`:

The setprogname() function sets the name of the program to be the last component of the progname argument.

The best would probably be to copy the implementation of `thisExePath`, but change it to open the file and return a file descriptor.

A file descriptor to the executable also seems to be available in the auxiliary vector on Linux [1]. This can be accessed with the function `getauxval` or directly after the `envp` parameter in the C main function. I see references to the auxiliary vector in *BSD systems as well. Might be worth checking out.

BTW, there does not seem to be a reliable way to get the path to the current executable on OpenBSD.

[1] http://articles.manugarg.com/aboutelfauxiliaryvectors.html [2] https://lwn.net/Articles/519085/

--
February 23
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #7 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Jacob Carlborg from comment #6)
> A file descriptor to the executable also seems to be available in the auxiliary vector on Linux [1]. This can be accessed with the function `getauxval` or directly after the `envp` parameter in the C main function. I see references to the auxiliary vector in *BSD systems as well. Might be worth checking out.

Interesting, though as far as I can see, that file descriptor is only available to interpreters, and is no longer there at the time that the interpreted program has begun execution. Otherwise, it would take up a file descriptor slot, and thus would be a very noticeable fourth addition to the standard stdin/stdout/stderr streams. So it doesn't look like we can use this unfortunately.

--
February 23
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #8 from Jacob Carlborg <doob@me.com> ---
You are right that the file descriptor (AT_EXECFD) won't work, I just tested that, it doesn't return a valid value. But, perhaps even better, it's possible to access the address of the program headers of the executable (AT_PHDR) [1]. Here's an experiment I did on Linux:

# cat main.d
import core.stdc.stdio;
import core.internal.elf.dl;

extern (C) ulong getauxval(ulong type);

void main()
{
    const aux = cast(void*) getauxval(3);
    const baseAddress = SharedObject.thisExecutable.baseAddress;
    printf("aux=%p baseAddress=%p diff=%ld\n", aux, baseAddress, aux -
baseAddress);
}

# dmd -run main.d
aux=0x55eb0f1e4040 baseAddress=0x55eb0f1e4000 diff=64

It returns a slightly different address than `SharedObject.thisExecutable.baseAddress`, but it's close enough that it cannot be a coincident. Perhaps someone with more knowledge of `core.internal.efl` can explain why the difference.

[1] https://man7.org/linux/man-pages/man3/getauxval.3.html

--
February 23
https://issues.dlang.org/show_bug.cgi?id=21656

--- Comment #9 from kinke <kinke@gmx.net> ---
I've just hit a related issue, a regression in master which was unfortunately cherry-picked for LDC 1.25 final: https://github.com/dlang/druntime/pull/3382

The problem in this specific case was that the unittest process changes the working dir (unit-threaded SandBox) and then an exception is thrown. The executable can thus not be opened anymore, and no file/line infos are available (if it wouldn't segfault before). I guess this scenario is 1000x more likely than the executable file being in Nirvana at the time an exception is thrown, and a real problem.

--