From: Ersek, Laszlo on
On Tue, 4 May 2010, William Ahern wrote:

> #include <stdlib.h>
> #include <stdio.h>
>
> #include <fcntl.h>
>
> #include <dlfcn.h>
>
> #include <err.h>
>
> int main(void) {
> char path[256] = "/usr/local/lib/libyaml.so.0.0";
> char name[64] = "yaml_get_version_string";
> int fd;
> void *lib;
> const char *(*getver)(void);
>
> if (-1 == (fd = open(path, O_RDONLY)))
> err(EXIT_FAILURE, "%s", path);
>
> snprintf(path, sizeof path, "/dev/fd/%d", fd);
>
> if (!(lib = dlopen(path, RTLD_NOW)))
> errx(EXIT_FAILURE, "%s: %s", path, dlerror());
>
> if (!(getver = dlsym(lib, name)))

I think it might be better (for some definition of "better") to write this
as

if (!(*(void **)&getver = dlsym(lib, name)))

See [0], [1], and [2].



> errx(EXIT_FAILURE, "%s: %s", name, dlerror());
>
> printf("%s\n", getver());
>
> return 0;
> }
>


Cheers,
lacos

[0] http://www.opengroup.org/onlinepubs/9699919799/functions/dlsym.html#tag_16_96_06
[1] http://www.opengroup.org/onlinepubs/9699919799/functions/dlsym.html#tag_16_96_08
[2] http://www.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_12_03
From: Ersek, Laszlo on
On Wed, 5 May 2010, Nicolas George wrote:

> David Schwartz wrote in message
> <f37d8057-9e6b-4e9a-a6fd-4371dadd683e(a)k25g2000prh.googlegroups.com>:

>> I think it's likely a better idea to check the ownership and
>> permissions of the directory the library is in before calling 'dlopen'.
>
> He needs to check every path element that leads to the library, because
> either one could be a symlink.
>
> And the check must be aware of the local system's rules for permissions.
> For example, just checking the file mode may not be enough if there are
> ACLs.

Once the full pathname has been tokenized, the way to do that might be
something like this. (Note - I didn't even try to compile this. It's here
only to invite criticism and/or better ideas.)

int
safeopen(const char * const * elem, size_t nelem)
{
if (0u < nelem) {
int fd;
size_t cur;

fd = open("/", O_SEARCH | O_DIRECTORY | O_NOFOLLOW);
cur = 0u;

loop:
if (-1 != fd) {
struct stat s;

if (0 == fstat(fd, &s)) {
if (cur == nelem) {
if (S_ISREG(s.st_mode) && /* other checks on the executable */) {
return fd;
}
}
else {
if (/* checks on the directory */) {
int tmp;

tmp = fd;
fd = openat(fd, elem[cur], O_NOFOLLOW
| (cur < nelem - 1u ? O_SEARCH | O_DIRECTORY : O_EXEC));

if (0 == close(tmp)) {
++cur;
goto loop;
}
}
}
}

if (-1 != fd) {
(void)close(fd);
}
}
}
return -1;
}

The returned int (if not -1) could be used with fexcve(). Unfortunately
(or not), there is no "dlfdopen()".

The code could hang indefinitely if the last component identified a FIFO.
I'd OR O_NONBLOCK with O_EXEC, if not for O_NONBLOCK being unspecified for
regular files. (I can't see anything in the openat() spec that would
require O_EXEC to fail on a FIFO (and immediately).)


> He also needs to check that the library has no runtime dependency
> towards untrusted paths. For example, with modern automagic tools, if
> someone compiles with --prefix=/tmp/install and then moves the
> installation (he'd rather use DESTDIR, of course), he can get
> RPATH=/tmp/install/lib in some of his libraries.
>
> Even if the library is in a completely trusted path and does not have
> structural dependencies towards untrusted paths, it could be a
> completely unrelated library unsuited to run in a SUID environment. Some
> libraries, for example, parse some specific environment variable in
> their _init function.

We've been at the same spot in the near past: a process asking the kernel
(or whomever) for protection from code it executes.

Cheers,
lacos
From: William Ahern on
Ersek, Laszlo <lacos(a)caesar.elte.hu> wrote:
> On Tue, 4 May 2010, William Ahern wrote:
<snip>
> > if (!(getver = dlsym(lib, name)))
>
> I think it might be better (for some definition of "better") to write this
> as
>
> if (!(*(void **)&getver = dlsym(lib, name)))

Mea culpa.

Recent versions of GCC enforce strict aliasing so rigorously that I
instinctively refrain from such tricks. Indeed, GCC 4.5 complains at -O2.

Being forced to type pun to get a valid lvalue for such common,
unobjectionable usage is ridiculous. The standard should make conversions
from void pointers to function pointers unspecified so GCC, et al wouldn't
have to emit a diagnostic when building in a POSIX environment. But there
are no changes along these lines in C1X AFAICT.

From: Geoff Clare on
William Ahern wrote:

> Ersek, Laszlo <lacos(a)caesar.elte.hu> wrote:
>> On Tue, 4 May 2010, William Ahern wrote:
> <snip>
>> > if (!(getver = dlsym(lib, name)))
>>
>> I think it might be better (for some definition of "better") to write this
>> as
>>
>> if (!(*(void **)&getver = dlsym(lib, name)))
>
> Mea culpa.
>
> Recent versions of GCC enforce strict aliasing so rigorously that I
> instinctively refrain from such tricks. Indeed, GCC 4.5 complains at -O2.

It is right to complain. That code would not work on an
implementation where pointer-to-void and pointer-to-function have
different representations.

The dlsym() example in POSIX that uses similar code is a bug in POSIX,
and is being corrected by this interpretation:

http://austingroupbugs.net/view.php?id=74#c205

which (among many other things) changes the relevant line in the
example from

*(void **)(&fptr) = dlsym(handle, "my_function");

back to

fptr = (int (*)(int))dlsym(handle, "my_function");

(which is how it was in SUSv2). This might also produce a warning
in some compilers, but implementations are required to accept it
and "do the right thing".

--
Geoff Clare <netnews(a)gclare.org.uk>


From: William Ahern on
Geoff Clare <geoff(a)clare.see-my-signature.invalid> wrote:
> William Ahern wrote:
>
> > Ersek, Laszlo <lacos(a)caesar.elte.hu> wrote:
> >> On Tue, 4 May 2010, William Ahern wrote:
> > <snip>
> >> > if (!(getver = dlsym(lib, name)))
> >>
> >> I think it might be better (for some definition of "better") to write this
> >> as
> >>
> >> if (!(*(void **)&getver = dlsym(lib, name)))
> >
> > Mea culpa.
> >
> > Recent versions of GCC enforce strict aliasing so rigorously that I
> > instinctively refrain from such tricks. Indeed, GCC 4.5 complains at -O2.

> It is right to complain. That code would not work on an
> implementation where pointer-to-void and pointer-to-function have
> different representations.

Except on POSIX systems they cannot--2.12.3--and this sort of type-punning
is the only way to get around the C standard.

> The dlsym() example in POSIX that uses similar code is a bug in POSIX,
> and is being corrected by this interpretation:

> http://austingroupbugs.net/view.php?id=74#c205

> which (among many other things) changes the relevant line in the
> example from

> *(void **)(&fptr) = dlsym(handle, "my_function");

> back to

> fptr = (int (*)(int))dlsym(handle, "my_function");

> (which is how it was in SUSv2). This might also produce a warning
> in some compilers, but implementations are required to accept it
> and "do the right thing".

Required by which standard? All conversions from a void pointer to a
function pointer are undefined behavior in C, and all implementations are
required to emit a diagnostic, and allowed to bail. If a function pointer is
wider than a void pointer, for instance, how does an explicit conversion
change anything? That's rhetorical, because it in fact doesn't, as far as
the C standard is concerned.

The problem with the C standard should be fixed. Even if POSIX/SUSv2
mandates certain behavior for an explicit conversion from void to function
pointer, the C standard requires at least a diagnostic, and on GCC and
others you can't use the highest level of warning and still build the file.
POSIX's use of type-punning, and the 2.12.3 requirement that void pointers
and functions pointers have the same representation, are ugly, yet the only
way to make a function like dlsym() available to a strictly conforming C
program.

I don't understand why the Austin Group would entertain returning to the
casting form. If the intention is to be able to drop the 2.12.3 requirement,
then they've just shot themselves in the foot.

First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4
Prev: How to find out the default gateway on Mac OSX?
Next: ack