[Libva] Weird behaviour when dup()'ing the file descriptor for /dev/dri/card0

Xiang, Haihao haihao.xiang at intel.com
Thu Jul 26 18:52:38 PDT 2012


On Wed, 2012-07-25 at 22:53 -0700, Jorge Lucangeli Obes wrote: 
> 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?

Yes.

> 
> 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.

Each struct file has only one 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