2020-06-03 17:49:44

by Alexander Potapenko

[permalink] [raw]
Subject: [PATCH] ovl: explicitly initialize error in ovl_copy_xattr()

Under certain circumstances (we found this out running Docker on a
Clang-built kernel with CONFIG_INIT_STACK_ALL) ovl_copy_xattr() may
return uninitialized value of |error| from ovl_copy_xattr().
It is then returned by ovl_create() to lookup_open(), which casts it to
an invalid dentry pointer, that can be further read or written by the
lookup_open() callers.

Signed-off-by: Alexander Potapenko <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Roy Yang <[email protected]>
Cc: <[email protected]> # 4.1

---

It's unclear to me whether error should be initially 0 or some error
code (both seem to work), but I thought returning an error makes sense,
as the situation wasn't anticipated by the code authors.

The bug seem to date back to at least v4.1 where the annotation has been
introduced (i.e. the compilers started noticing error could be used
before being initialized). I hovever didn't try to prove that the
problem is actually reproducible on such ancient kernels. We've seen it
on a real machine running v4.4 as well.
---
fs/overlayfs/copy_up.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 9709cf22cab3..428d43e2d016 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{
ssize_t list_size, size, value_size = 0;
char *buf, *name, *value = NULL;
- int uninitialized_var(error);
+ int error = -EINVAL;
size_t slen;

if (!(old->d_inode->i_opflags & IOP_XATTR) ||
--
2.27.0.rc2.251.g90737beb825-goog


2020-06-03 20:37:18

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] ovl: explicitly initialize error in ovl_copy_xattr()

On Wed, Jun 03, 2020 at 07:47:14PM +0200, [email protected] wrote:
> Under certain circumstances (we found this out running Docker on a
> Clang-built kernel with CONFIG_INIT_STACK_ALL) ovl_copy_xattr() may
> return uninitialized value of |error| from ovl_copy_xattr().
> It is then returned by ovl_create() to lookup_open(), which casts it to
> an invalid dentry pointer, that can be further read or written by the
> lookup_open() callers.
>
> Signed-off-by: Alexander Potapenko <[email protected]>

Link: https://bugs.chromium.org/p/chromium/issues/detail?id=1050405
Fixes: e4ad29fa0d22 ("ovl: use a minimal buffer in ovl_copy_xattr")
Cc: [email protected]
Reviewed-by: Kees Cook <[email protected]>

It seems the error isn't reported anywhere, so the value likely isn't
too important. -EINVAL seems sane to me.

Thought: should CONFIG_INIT_STACK_ALL=y disable uninitialized_var()?

$ git grep uninitialized_var | wc -l
300

We have evidence this is being used inappropriately and is masking bugs.
I would actually think it should should be removed globally, but it
seems especially important for CONFIG_INIT_STACK_ALL=y.

I've opened:
https://github.com/KSPP/linux/issues/81

--
Kees Cook

2020-06-03 21:52:30

by Vivek Goyal

[permalink] [raw]
Subject: Re: [PATCH] ovl: explicitly initialize error in ovl_copy_xattr()

On Wed, Jun 03, 2020 at 07:47:14PM +0200, [email protected] wrote:
> Under certain circumstances (we found this out running Docker on a
> Clang-built kernel with CONFIG_INIT_STACK_ALL) ovl_copy_xattr() may
> return uninitialized value of |error| from ovl_copy_xattr().

If we are returning uninitialized value of error, doesn't that mean
that somewhere in the function we are returning without setting error.
And that probably means that's a bug and we should fix it?

I am wondering if this is triggered by loop finishing because all
the xattr on the file are ovl_is_private_xattr(). In that case, we
will come out of the loop without setting error. This is in fact
success and we should return 0 instead of some random error?

Thanks
Vivek


> It is then returned by ovl_create() to lookup_open(), which casts it to
> an invalid dentry pointer, that can be further read or written by the
> lookup_open() callers.
>
> Signed-off-by: Alexander Potapenko <[email protected]>
> Cc: Kees Cook <[email protected]>
> Cc: Roy Yang <[email protected]>
> Cc: <[email protected]> # 4.1
>
> ---
>
> It's unclear to me whether error should be initially 0 or some error
> code (both seem to work), but I thought returning an error makes sense,
> as the situation wasn't anticipated by the code authors.
>
> The bug seem to date back to at least v4.1 where the annotation has been
> introduced (i.e. the compilers started noticing error could be used
> before being initialized). I hovever didn't try to prove that the
> problem is actually reproducible on such ancient kernels. We've seen it
> on a real machine running v4.4 as well.
> ---
> fs/overlayfs/copy_up.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
> index 9709cf22cab3..428d43e2d016 100644
> --- a/fs/overlayfs/copy_up.c
> +++ b/fs/overlayfs/copy_up.c
> @@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
> {
> ssize_t list_size, size, value_size = 0;
> char *buf, *name, *value = NULL;
> - int uninitialized_var(error);
> + int error = -EINVAL;
> size_t slen;
>
> if (!(old->d_inode->i_opflags & IOP_XATTR) ||
> --
> 2.27.0.rc2.251.g90737beb825-goog
>

2020-06-04 08:31:37

by Alexander Potapenko

[permalink] [raw]
Subject: Re: [PATCH] ovl: explicitly initialize error in ovl_copy_xattr()

On Wed, Jun 3, 2020 at 11:46 PM Vivek Goyal <[email protected]> wrote:
>
> On Wed, Jun 03, 2020 at 07:47:14PM +0200, [email protected] wrote:
> > Under certain circumstances (we found this out running Docker on a
> > Clang-built kernel with CONFIG_INIT_STACK_ALL) ovl_copy_xattr() may
> > return uninitialized value of |error| from ovl_copy_xattr().
>
> If we are returning uninitialized value of error, doesn't that mean
> that somewhere in the function we are returning without setting error.
> And that probably means that's a bug and we should fix it?

Could be. My understanding of that code is quite limited, so I'm happy
to change the patch if necessary.

> I am wondering if this is triggered by loop finishing because all
> the xattr on the file are ovl_is_private_xattr().

Yes, that's the case. The loop makes one iteration, then
ovl_is_private_xattr() returns true, then the loop ends.

> In that case, we
> will come out of the loop without setting error. This is in fact
> success and we should return 0 instead of some random error?

Thanks for letting me know. I'll change that to 0 then.

> Thanks
> Vivek
>
>
> > It is then returned by ovl_create() to lookup_open(), which casts it to
> > an invalid dentry pointer, that can be further read or written by the
> > lookup_open() callers.
> >
> > Signed-off-by: Alexander Potapenko <[email protected]>
> > Cc: Kees Cook <[email protected]>
> > Cc: Roy Yang <[email protected]>
> > Cc: <[email protected]> # 4.1
> >
> > ---
> >
> > It's unclear to me whether error should be initially 0 or some error
> > code (both seem to work), but I thought returning an error makes sense,
> > as the situation wasn't anticipated by the code authors.
> >
> > The bug seem to date back to at least v4.1 where the annotation has been
> > introduced (i.e. the compilers started noticing error could be used
> > before being initialized). I hovever didn't try to prove that the
> > problem is actually reproducible on such ancient kernels. We've seen it
> > on a real machine running v4.4 as well.
> > ---
> > fs/overlayfs/copy_up.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
> > index 9709cf22cab3..428d43e2d016 100644
> > --- a/fs/overlayfs/copy_up.c
> > +++ b/fs/overlayfs/copy_up.c
> > @@ -47,7 +47,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
> > {
> > ssize_t list_size, size, value_size = 0;
> > char *buf, *name, *value = NULL;
> > - int uninitialized_var(error);
> > + int error = -EINVAL;
> > size_t slen;
> >
> > if (!(old->d_inode->i_opflags & IOP_XATTR) ||
> > --
> > 2.27.0.rc2.251.g90737beb825-goog
> >
>


--
Alexander Potapenko
Software Engineer

Google Germany GmbH
Erika-Mann-Straße, 33
80636 München

Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg