[Libva] Weird behaviour when dup()'ing the file descriptor for /dev/dri/card0
Jorge Lucangeli Obes
jorgelo at chromium.org
Wed Jul 25 22:53:06 PDT 2012
On Wed, Jul 25, 2012 at 10:28 PM, Xiang, Haihao <haihao.xiang at intel.com> wrote:
> On Wed, 2012-07-25 at 21:15 -0700, Jorge Lucangeli Obes wrote:
>> On Wed, Jul 25, 2012 at 8:50 PM, Xiang, Haihao <haihao.xiang at intel.com> wrote:
>> > On Wed, 2012-07-25 at 15:21 -0700, Jorge Lucangeli Obes wrote:
>> >> Hi all,
>> >>
>> >> My name is Jorge. I'm trying to get libva to work inside the syscall
>> >> filtering sandbox (seccomp mode 2 [1], landed on Linux 3.5) in
>> >> Chromium/Chromium OS. Syscall filtering in Chromium allows us to deny
>> >> access to syscalls deemed "dangerous" (e.g. ptrace()), or syscalls
>> >> that could be used by malicious code after compromising a Chromium
>> >> process using a vulnerability in the code.
>> >>
>> >> More specifically, we would like to have Chromium's GPU process run
>> >> without unlimited access to the open() system call. To do this, we
>> >> have to warm up/preload some resources, including the
>> >> i965_drv_video.so library and /dev/dri/card0 which are legitimately
>> >> opened by libva. The issue I'm hitting is with /dev/dri/card0.
>> >>
>> >> The way we limit access to open() is by preopening the files needed by
>> >> libva, and then hooking open() when the sandbox is enabled, and
>> >> returning dup()'s of the original fd's opened before enabling the
>> >> sandbox. One of the files libva opens is /dev/dri/card0:
>> >>
>> >> http://cgit.freedesktop.org/vaapi/libva/tree/va/x11/dri2_util.c#n198
>> >> va/x11/dri2_util.c
>> >>
>> >> Bool
>> >> isDRI2Connected(VADriverContextP ctx, char **driver_name)
>> >> {
>> >> /* snip */
>> >> dri_state->fd = open(device_name, O_RDWR); <--- device_name ==
>> >> "/dev/dri/card0"
>> >> assert(dri_state->fd >= 0);
>> >>
>> >> /* snip */
>> >>
>> >> if (drmGetMagic(dri_state->fd, &magic))
>> >> goto err_out;
>> >>
>> >> if (!VA_DRI2Authenticate(ctx->native_dpy,
>> >> RootWindow(ctx->native_dpy, ctx->x11_screen),
>> >> magic)) <--- failing
>> >> goto err_out;
>> >> /* snip */
>> >>
>> >> Now, the issue that I'm seeing is the following: the call to
>> >> open(device_name) happens after our sandbox is on, so the call gets
>> >> intercepted. Instead of letting it go through, we dup() a fd for
>> >> |device_name| that we opened before enabling the sandbox. drmGetMagic
>> >> succeeds when called on that dup()'ed fd (all it does is an ioctl),
>> >> but for some reason the call to DRI2Authenticate fails. This makes
>> >> va_getDriverName() fail as well, and Chromium falls back to not using
>> >> libva. I've tried using the LIBVA_DRIVER_NAME env var to sidestep the
>> >> call to va_getDriverName() but since va_getDriverName() does a whole
>> >> bunch of things, sidestepping makes an assert trigger in the actual
>> >> libva Intel driver code.
>> >
>> > Yes, because a client can't access /dev/dri/card0 without authentication
>> >
>> >>
>> >> Stracing the behaviour with the open() restrictions disabled and
>> >> enabled, the difference appears to be in what the driver reads from fd
>> >> 5, which is a socket. My question would be, then, if anyone knows
>> >> whether reopening /dev/dri/card0 clears or resets some state, which
>> >> could be causing DRIAuthenticate to fail when called after calling
>> >> drmGetMagic on a dup()'ed fd.
>> >>
>> >> *Open hook disabled:
>> >> open("/dev/dri/card0", O_RDWR) = 15
>> >> ...
>> >> dup(15) = 38
>> >> ioctl(38, 0x80046402, 0x7fff7ee87360) = 0
>> >> poll([{fd=5, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=5, revents=POLLOUT}])
>> >> writev(5, [{"\204\2\3\0\255\0\0\0\n\0\0\0", 12}, {NULL, 0}, {"", 0}], 3) = 12
>> >> poll([{fd=5, events=POLLIN}], 1, -1) = 1 ([{fd=5, revents=POLLIN}])
>> >> read(5, "\1\0a\1\0\0\0\0\1\0\0\0\t\0\0\0\0\0\0\0\0\0\0\0\260O\374\364y\177\0\0",
>> >> 4096) = 32
>> >> ...
>> >> write(2, "libva: ", 7) = 7
>> >> write(2, "va_getDriverName() returns 0\n", 29) = 29
>> >>
>> >> *Open hook enabled:
>> >> open("/dev/dri/card0", O_RDWR) = 15
>> >> ...
>> >> dup(15) = 32
>> >> ioctl(32, 0x80046402, 0x7fff7ee87360) = 0
>> >> poll([{fd=5, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=5, revents=POLLOUT}])
>> >> writev(5, [{"\204\2\3\0\255\0\0\0\n\0\0\0", 12}, {NULL, 0}, {"", 0}], 3) = 12
>> >> poll([{fd=5, events=POLLIN}], 1, -1) = 1 ([{fd=5, revents=POLLIN}])
>> >> read(5, "\1\0A\4\0\0\0\0\0\0\0\0\t\0\0\0
>> >> \340\n\365y\177\0\0\260O\374\364y\177\0\0", 4096) = 32
>> >> ...
>> >> write(2, "DRI2Authenticate failed\n", 24) = 24
>> >> close(32) = 0
>> >> ...
>> >> write(2, "libva: ", 7) = 7
>> >> write(2, "va_getDriverName() returns -1\n", 30) = 30
>> >
>> > a fd and a dup()'ed fd share the same struct file and the magic number
>> > will be removed from master after being authenticated (drmAuthMagic()).
>> >
>> > So one possible is it has been authenticated on the fd elsewhere.
>>
>> Good, that's what we thought. It's definitely the case that the same
>> magic number is being used to authenticate more than once, and all
>> attempts after the first one predictably fail.
>>
>> So, the next question would be: is there any way, besides reopening
>> the card, to get a new magic number? Any other file operation that can
>> be done on the card fd?
>
> If you make sure it has been authenticated, you can remove the
> authentication code from isDRI2Connected(). It should work.
So, just to check that I'm understanding correctly: the card only
needs to be authenticated once every boot?
Reopening /dev/dri/card0 will populate a new magic number in the new
struct file corresponding to that open() call. I was wondering, then,
if there was another file operation, which could be done on an already
opened file descriptor, which would mimic the actions carried out on
open() and allow the same struct file to get a new magic number.
I'll take a look at the driver code.
Thanks!
Jorge
>> Thanks for the help!
>> Jorge
>>
>> > f.g.
>> >
>> > dri_state->fd = open(device_name, O_RDWR);
>> >
>> > if (dri_state->fd < 0)
>> > goto err_out;
>> >
>> > if (drmGetMagic(dri_state->fd, &magic)) <<=== success, return a magic number.
>> > goto err_out;
>> >
>> > if (!VA_DRI2Authenticate(ctx->native_dpy, RootWindow(ctx->native_dpy, ctx->x11_screen),
>> > magic)) <<=== success, and remove the magic number from master.
>> > goto err_out;
>> >
>> > dri_state->fd = dup(dri_state->fd);
>> > assert(dri_state->fd >= 0);
>> >
>> > if (drmGetMagic(dri_state->fd, &magic)) <<=== success, return the same magic number because they are same the same struct file
>> > goto err_out;
>> >
>> > if (!VA_DRI2Authenticate(ctx->native_dpy, RootWindow(ctx->native_dpy, ctx->x11_screen),
>> > magic)) <<== fail, the magic number has been removed from master before this operation
>> > goto err_out;
>> >
>> > Regards
>> > Haihao
>> >
>> >>
>> >> Thanks!
>> >> Jorge
>> >>
>> >> [1] http://lwn.net/Articles/475043/
>> >> _______________________________________________
>> >> Libva mailing list
>> >> Libva at lists.freedesktop.org
>> >> http://lists.freedesktop.org/mailman/listinfo/libva
>> >
>> >
>
>
More information about the Libva
mailing list