Re: [RFC] regset ->get() API

From: Linus Torvalds
Date: Thu Feb 20 2020 - 17:56:49 EST

On Thu, Feb 20, 2020 at 2:47 PM Al Viro <viro@xxxxxxxxxxxxxxxxxx> wrote:
> On Wed, Feb 19, 2020 at 12:01:54PM -0800, Linus Torvalds wrote:
> > I don't mind it, but some of those buffers are big, and the generic
> > code generally doesn't know how big.
> That's what regset_size() returns...

Yes, but the code ends up being disgusting. You first have to call
that indirect function just to get the size, then do a kmalloc, and
then call another indirect function to actually fill it.

Don't do that. Not since we know how retpoline is a bad thing.

And since the size isn't always some trivial constant (ie for x86 PFU
it depends on the register state!), I think the only sane model is to
change the interface even more, and just have the "get()" function not
only get the data, but allocate the backing store too.

So you'd never pass in the result pointer - you'd get a result area
that you can then free.


The alternative is to pick a constant size that is "big enough", and
just assume that one page (or whatever) is sufficient:

> > Maybe even more. I'm not sure how big the FPU regset can get on x86...
> amd64:
> REGSET_GENERAL => sizeof(struct user_regs_struct) (216)
> REGSET_FP => sizeof(struct user_i387_struct) (512)
> REGSET_XSTATE => sizeof(struct swregs_state) or
> sizeof(struct fxregs_state) or
> sizeof(struct fregs_state) or
> XSAVE insn buffer size (max about 2.5Kb, AFAICS)

Yeah, so apparently one page isn't sufficient.

> FWIW, what I have in mind is to start with making copy_regset_to_user() do
> buf = kmalloc(size, GFP_KERNEL);
> if (!buf)
> return -ENOMEM;
> err = regset->get(target, regset, offset, size, buf, NULL);

See above. This doesn't work. You don't know the size. And we don't
have a known maximum size either.

> if (!err && copy_to_user(data, buf, size))
> err = -EFAULT;
> kfree(buf);
> return err;

But if you change "->get()" to just return a kmalloc'ed buffer, I'd be
ok with that.

IOW, something like

buf = regset->get(target, regset, &size);
if (IS_ERR(buf))
return PTR_ERR(bug);
err = copy_to_user(data, buf, size);
return err;

or something like that. Just get rid of the "ubuf" entirely.

Wouldn't that be nicer?