[Linux-kernel-mentees] [PATCH] v4l2-tpg: Fix global-out-of-bounds read in precalculate_color()

Peilin Ye yepeilin.cs at gmail.com
Fri Aug 21 09:48:13 UTC 2020

Hi Mr. Verkuil,

On Wed, Aug 19, 2020 at 04:26:28PM +0200, Hans Verkuil wrote:
> Hi Peilin,
> On 10/08/2020 07:05, Peilin Ye wrote:
> > precalculate_color() is reading out of `sin` since `tpg->hue` is not being
> > properly checked. Fix it. `cos` is safe, as long as `tpg->hue` is higher
> > than or equal to -192.
> Thank you for this patch, but there is something I don't understand, namely
> just *how* tpg->hue can be out-of-range.
> From what I can see vivid sets hue via tpg_s_hue() when the V4L2_CID_HUE control
> is set. But that control has a range of -128...128, so ctrl->val should always be in
> that range.
> I would really like to know 1) what the value of tpg->hue actually is when it goes
> out of range, and 2) who is changing it to that value. Can you do a bit more digging?

The value of `tpg->hue` was -20551. It came from the userspace, see the
"\xb9\xaf" on line 500 of the reproducer:


  NONFAILING(memcpy((void*)0x20000200, "/dev/video6\000\000", 13));
  res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000200ul, 2ul, 0ul);
  if (res != -1)
    r[0] = res;
                     ^^^^    ^^^^    ^^^^    ^^^^^^^^
  syscall(__NR_write, r[0], 0x20000140ul, 8ul);

I guess the root cause is a race condition in the vivid test driver,
which completely corrupted `tpg`. I see bytes like "\x4d", "\x66" and
"\xfd" around `tpg->hue`, too.

The reproducer does two things: the above write() on /dev/video6, and a
preadv() on /dev/video3:

  NONFAILING(*(uint64_t*)0x20000800 = 0x20000000);
  NONFAILING(*(uint64_t*)0x20000808 = 0x1f);
  NONFAILING(*(uint64_t*)0x20000810 = 0);
  NONFAILING(*(uint64_t*)0x20000818 = 0);
  NONFAILING(*(uint64_t*)0x20000820 = 0);
  NONFAILING(*(uint64_t*)0x20000828 = 0);
  NONFAILING(*(uint64_t*)0x20000830 = 0);
  NONFAILING(*(uint64_t*)0x20000838 = 0);
  NONFAILING(*(uint64_t*)0x20000840 = 0);
  NONFAILING(*(uint64_t*)0x20000848 = 0);
  syscall(__NR_preadv, r[1], 0x20000800ul, 5ul, 0ul);

I commented out this preadv(), then the reproducer didn't cause any
crash. Unfortunately I don't know the code well enough in order to
figure out exactly why...At this point of time I'd like to send you an
v2 as you suggested, it should work as a mitigation.

Thank you for the suggestion!

Peilin Ye

More information about the Linux-kernel-mentees mailing list