2019-10-26 07:53:14

by zhanglin

[permalink] [raw]
Subject: [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

memset() the structure ethtool_wolinfo that has padded bytes
but the padded bytes have not been zeroed out.

Signed-off-by: zhanglin <[email protected]>
---
net/core/ethtool.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index aeabc48..563a845 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)

static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
{
- struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+ struct ethtool_wolinfo wol;

if (!dev->ethtool_ops->get_wol)
return -EOPNOTSUPP;

+ memset(&wol, 0, sizeof(struct ethtool_wolinfo));
+ wol.cmd = ETHTOOL_GWOL;
dev->ethtool_ops->get_wol(dev, &wol);

if (copy_to_user(useraddr, &wol, sizeof(wol)))
--
2.15.2


2019-10-26 14:27:13

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Sat, Oct 26, 2019 at 03:54:16PM +0800, zhanglin wrote:
> memset() the structure ethtool_wolinfo that has padded bytes
> but the padded bytes have not been zeroed out.
>
> Signed-off-by: zhanglin <[email protected]>
> ---
> net/core/ethtool.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> index aeabc48..563a845 100644
> --- a/net/core/ethtool.c
> +++ b/net/core/ethtool.c
> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
>
> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> {
> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> + struct ethtool_wolinfo wol;
>

How did you detect that they weren't initialized? Is this a KASAN
thing?

Most of the time GCC will zero out the padding bytes when you have an
initializer like this, but sometimes it just makes the intialization a
series of assignments which leaves the holes uninitialized. I wish I
knew the rules so that I could check for it in Smatch. Or even better,
I wish that there were an option to always zero the holes in this
situation...

regards,
dan carpenter

2019-10-26 15:53:41

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Sat, 2019-10-26 at 17:24 +0300, Dan Carpenter wrote:
> On Sat, Oct 26, 2019 at 03:54:16PM +0800, zhanglin wrote:
> > memset() the structure ethtool_wolinfo that has padded bytes
> > but the padded bytes have not been zeroed out.
[]
> > diff --git a/net/core/ethtool.c b/net/core/ethtool.c
[]
> > @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> >
> > static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > {
> > - struct ethtool_wolinf wol = { .cmd = ETHTOOL_GWOL };
> > + struct ethtool_wolinfo wol;
> >
>
> How did you detect that they weren't initialized? Is this a KASAN
> thing?
>
> Most of the time GCC will zero out the padding bytes when you have an
> initializer like this, but sometimes it just makes the intialization a
> series of assignments which leaves the holes uninitialized. I wish I
> knew the rules so that I could check for it in Smatch. Or even better,
> I wish that there were an option to always zero the holes in this
> situation...

The standard doesn't specify what happens to the padding so
it's not just for gcc, it's compiler dependent.

So anything that's used in a copy_to_user with any possible
padding should either be zalloc'd or memset before assigned.

In this case:

include/uapi/linux/ethtool.h:#define SOPASS_MAX 6

and

include/uapi/linux/ethtool.h:struct ethtool_wolinfo {
include/uapi/linux/ethtool.h- __u32 cmd;
include/uapi/linux/ethtool.h- __u32 supported;
include/uapi/linux/ethtool.h- __u32 wolopts;
include/uapi/linux/ethtool.h- __u8 sopass[SOPASS_MAX];
include/uapi/linux/ethtool.h-};

so there's likely a couple bytes of trailing padding.


2019-10-26 18:22:15

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

From: zhanglin <[email protected]>
Date: Sat, 26 Oct 2019 15:54:16 +0800

> memset() the structure ethtool_wolinfo that has padded bytes
> but the padded bytes have not been zeroed out.
>
> Signed-off-by: zhanglin <[email protected]>

Applied and queued up for -stable.

2019-10-26 19:41:49

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> memset() the structure ethtool_wolinfo that has padded bytes
> but the padded bytes have not been zeroed out.
[]
> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
[]
> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
>
> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> {
> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> + struct ethtool_wolinfo wol;
>
> if (!dev->ethtool_ops->get_wol)
> return -EOPNOTSUPP;
>
> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> + wol.cmd = ETHTOOL_GWOL;
> dev->ethtool_ops->get_wol(dev, &wol);
>
> if (copy_to_user(useraddr, &wol, sizeof(wol)))

It seems likely there are more of these.

Is there any way for coccinelle to find them?

There are ~4000 structs in include/uapi and
there are ~3000 uses of copy_to_user in the tree.

$ git grep -P '\bstruct\s+\w+\s*{' include/uapi/ | cut -f2 -d" "|sort|uniq|wc -l
3785
$ git grep -w copy_to_user|wc -l
2854

A trivial grep and manual search using:

$ git grep -B20 -w copy_to_user | grep -A20 -P '\bstruct\s+\w+\s*=\s*{'

shows at least 1 (I didn't look very hard and stopped after finding 1):

include/uapi/linux/utsname.h:struct oldold_utsname {
include/uapi/linux/utsname.h- char sysname[9];
include/uapi/linux/utsname.h- char nodename[9];
include/uapi/linux/utsname.h- char release[9];
include/uapi/linux/utsname.h- char version[9];
include/uapi/linux/utsname.h- char machine[9];
include/uapi/linux/utsname.h-};

and

kernel/sys.c- struct oldold_utsname tmp = {};
kernel/sys.c-
kernel/sys.c- if (!name)
kernel/sys.c- return -EFAULT;
kernel/sys.c-
kernel/sys.c- down_read(&uts_sem);
kernel/sys.c- memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
kernel/sys.c- memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
kernel/sys.c- memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
kernel/sys.c- memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
kernel/sys.c- memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
kernel/sys.c- up_read(&uts_sem);
kernel/sys.c: if (copy_to_user(name, &tmp, sizeof(tmp)))

where there is likely 3 bytes of padding after 45 bytes of data
in the struct.


2019-10-26 20:18:58

by Julia Lawall

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()



On Sat, 26 Oct 2019, Joe Perches wrote:

> On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> > memset() the structure ethtool_wolinfo that has padded bytes
> > but the padded bytes have not been zeroed out.
> []
> > diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> []
> > @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> >
> > static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > {
> > - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> > + struct ethtool_wolinfo wol;
> >
> > if (!dev->ethtool_ops->get_wol)
> > return -EOPNOTSUPP;
> >
> > + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> > + wol.cmd = ETHTOOL_GWOL;
> > dev->ethtool_ops->get_wol(dev, &wol);
> >
> > if (copy_to_user(useraddr, &wol, sizeof(wol)))
>
> It seems likely there are more of these.
>
> Is there any way for coccinelle to find them?
>
> There are ~4000 structs in include/uapi and
> there are ~3000 uses of copy_to_user in the tree.
>
> $ git grep -P '\bstruct\s+\w+\s*{' include/uapi/ | cut -f2 -d" "|sort|uniq|wc -l
> 3785
> $ git grep -w copy_to_user|wc -l
> 2854
>
> A trivial grep and manual search using:
>
> $ git grep -B20 -w copy_to_user | grep -A20 -P '\bstruct\s+\w+\s*=\s*{'
>
> shows at least 1 (I didn't look very hard and stopped after finding 1):
>
> include/uapi/linux/utsname.h:struct oldold_utsname {
> include/uapi/linux/utsname.h- char sysname[9];
> include/uapi/linux/utsname.h- char nodename[9];
> include/uapi/linux/utsname.h- char release[9];
> include/uapi/linux/utsname.h- char version[9];
> include/uapi/linux/utsname.h- char machine[9];
> include/uapi/linux/utsname.h-};
>
> and
>
> kernel/sys.c- struct oldold_utsname tmp = {};
> kernel/sys.c-
> kernel/sys.c- if (!name)
> kernel/sys.c- return -EFAULT;
> kernel/sys.c-
> kernel/sys.c- down_read(&uts_sem);
> kernel/sys.c- memcpy(&tmp.sysname, &utsname()->sysname, __OLD_UTS_LEN);
> kernel/sys.c- memcpy(&tmp.nodename, &utsname()->nodename, __OLD_UTS_LEN);
> kernel/sys.c- memcpy(&tmp.release, &utsname()->release, __OLD_UTS_LEN);
> kernel/sys.c- memcpy(&tmp.version, &utsname()->version, __OLD_UTS_LEN);
> kernel/sys.c- memcpy(&tmp.machine, &utsname()->machine, __OLD_UTS_LEN);
> kernel/sys.c- up_read(&uts_sem);
> kernel/sys.c: if (copy_to_user(name, &tmp, sizeof(tmp)))
>
> where there is likely 3 bytes of padding after 45 bytes of data
> in the struct.

I looked into this at one point, but didn't get as far as generating
patches. I think that the approach was roughly to collect the types of
the fields, and then generate code that would use BUILD_BUG_ON to complain
if the sum of the sizes was not the same as the size of the structure.
The problem was that I wasn't sure what was a real problem, nor what was
the best way to solve it.

julia

Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On 26.10.19 21:40, Joe Perches wrote:
> On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
>> memset() the structure ethtool_wolinfo that has padded bytes
>> but the padded bytes have not been zeroed out.
> []
>> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> []
>> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
>>
>> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
>> {
>> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
>> + struct ethtool_wolinfo wol;
>>
>> if (!dev->ethtool_ops->get_wol)
>> return -EOPNOTSUPP;
>>
>> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
>> + wol.cmd = ETHTOOL_GWOL;
>> dev->ethtool_ops->get_wol(dev, &wol);
>>
>> if (copy_to_user(useraddr, &wol, sizeof(wol)))
>
> It seems likely there are more of these.
>
> Is there any way for coccinelle to find them?

Just curios: is static struct initialization (on stack) something that
should be avoided ? I've been under the impression that static
initialization allows thinner code and gives the compiler better chance
for optimizations.


--mtx

---
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
[email protected] -- +49-151-27565287

2019-11-21 11:21:40

by Michal Kubecek

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Thu, Nov 21, 2019 at 11:23:34AM +0100, Enrico Weigelt, metux IT consult wrote:
> On 26.10.19 21:40, Joe Perches wrote:
> > On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> >> memset() the structure ethtool_wolinfo that has padded bytes
> >> but the padded bytes have not been zeroed out.
> > []
> >> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> > []
> >> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> >>
> >> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> >> {
> >> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> >> + struct ethtool_wolinfo wol;
> >>
> >> if (!dev->ethtool_ops->get_wol)
> >> return -EOPNOTSUPP;
> >>
> >> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> >> + wol.cmd = ETHTOOL_GWOL;
> >> dev->ethtool_ops->get_wol(dev, &wol);
> >>
> >> if (copy_to_user(useraddr, &wol, sizeof(wol)))
> >
> > It seems likely there are more of these.
> >
> > Is there any way for coccinelle to find them?
>
> Just curios: is static struct initialization (on stack) something that
> should be avoided ? I've been under the impression that static
> initialization allows thinner code and gives the compiler better chance
> for optimizations.

Not in general. The (potential) problem here is that the structure has
padding and it is as a whole (i.e. including the padding) copied to
userspace. While I'm not aware of a compiler that wouldn't actually
initialize the whole data block including the padding in this case, the
C standard provides no guarantee about that so that to be sure we cannot
leak leftover kernel data to userspace, we need to explicitly initialize
the whole block.

If the structure is not going to be copied to userspace (or otherwise
exposed), using the initializer is fully sufficient and looks cleaner.

Michal Kubecek

2019-11-21 12:03:18

by Julia Lawall

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()



On Thu, 21 Nov 2019, Michal Kubecek wrote:

> On Thu, Nov 21, 2019 at 11:23:34AM +0100, Enrico Weigelt, metux IT consult wrote:
> > On 26.10.19 21:40, Joe Perches wrote:
> > > On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> > >> memset() the structure ethtool_wolinfo that has padded bytes
> > >> but the padded bytes have not been zeroed out.
> > > []
> > >> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> > > []
> > >> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> > >>
> > >> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > >> {
> > >> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> > >> + struct ethtool_wolinfo wol;
> > >>
> > >> if (!dev->ethtool_ops->get_wol)
> > >> return -EOPNOTSUPP;
> > >>
> > >> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> > >> + wol.cmd = ETHTOOL_GWOL;
> > >> dev->ethtool_ops->get_wol(dev, &wol);
> > >>
> > >> if (copy_to_user(useraddr, &wol, sizeof(wol)))
> > >
> > > It seems likely there are more of these.
> > >
> > > Is there any way for coccinelle to find them?
> >
> > Just curios: is static struct initialization (on stack) something that
> > should be avoided ? I've been under the impression that static
> > initialization allows thinner code and gives the compiler better chance
> > for optimizations.
>
> Not in general. The (potential) problem here is that the structure has
> padding and it is as a whole (i.e. including the padding) copied to
> userspace. While I'm not aware of a compiler that wouldn't actually
> initialize the whole data block including the padding in this case, the
> C standard provides no guarantee about that so that to be sure we cannot
> leak leftover kernel data to userspace, we need to explicitly initialize
> the whole block.

I'm not sure that it is likely that the compiler will do anything other
than ensure that all the fields are initialized. Among the files that I
could compile, the only case with actual padding and no memset, memcpy,
copy_from_user or structure assignment that I could find was

drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c

There is the code struct drm_amdgpu_info_device dev_info = {};

but I couldn't see any thing in the assembly language that seemed to zero
the structure. gcc probably thought its job was done because all fields
are subsequently initialized. But the size of the structure does not seem
to be the same as the sum of the size of the fields.

The set of fields was collected with Coccinelle, which could be unreliable
for this task.

julia

>
> If the structure is not going to be copied to userspace (or otherwise
> exposed), using the initializer is fully sufficient and looks cleaner.
>
> Michal Kubecek
> _______________________________________________
> Cocci mailing list
> [email protected]
> https://systeme.lip6.fr/mailman/listinfo/cocci
>

2019-11-21 12:11:57

by Dan Carpenter

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Thu, Nov 21, 2019 at 12:19:17PM +0100, Michal Kubecek wrote:
> On Thu, Nov 21, 2019 at 11:23:34AM +0100, Enrico Weigelt, metux IT consult wrote:
> > On 26.10.19 21:40, Joe Perches wrote:
> > > On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> > >> memset() the structure ethtool_wolinfo that has padded bytes
> > >> but the padded bytes have not been zeroed out.
> > > []
> > >> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> > > []
> > >> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> > >>
> > >> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > >> {
> > >> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> > >> + struct ethtool_wolinfo wol;
> > >>
> > >> if (!dev->ethtool_ops->get_wol)
> > >> return -EOPNOTSUPP;
> > >>
> > >> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> > >> + wol.cmd = ETHTOOL_GWOL;
> > >> dev->ethtool_ops->get_wol(dev, &wol);
> > >>
> > >> if (copy_to_user(useraddr, &wol, sizeof(wol)))
> > >
> > > It seems likely there are more of these.
> > >
> > > Is there any way for coccinelle to find them?
> >
> > Just curios: is static struct initialization (on stack) something that
> > should be avoided ? I've been under the impression that static
> > initialization allows thinner code and gives the compiler better chance
> > for optimizations.
>
> Not in general. The (potential) problem here is that the structure has
> padding and it is as a whole (i.e. including the padding) copied to
> userspace. While I'm not aware of a compiler that wouldn't actually
> initialize the whole data block including the padding in this case, the
> C standard provides no guarantee about that so that to be sure we cannot
> leak leftover kernel data to userspace, we need to explicitly initialize
> the whole block.

GCC will not always initialize the struct holes. This patch fixes a
real bug that GCC on my system (v7.4)

regards,
dan carpenter

2019-11-21 13:42:21

by Michal Kubecek

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()

On Thu, Nov 21, 2019 at 03:07:33PM +0300, Dan Carpenter wrote:
> On Thu, Nov 21, 2019 at 12:19:17PM +0100, Michal Kubecek wrote:
> > On Thu, Nov 21, 2019 at 11:23:34AM +0100, Enrico Weigelt, metux IT consult wrote:
> > > On 26.10.19 21:40, Joe Perches wrote:
> > > > On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> > > >> memset() the structure ethtool_wolinfo that has padded bytes
> > > >> but the padded bytes have not been zeroed out.
> > > > []
> > > >> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> > > > []
> > > >> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> > > >>
> > > >> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > > >> {
> > > >> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> > > >> + struct ethtool_wolinfo wol;
> > > >>
> > > >> if (!dev->ethtool_ops->get_wol)
> > > >> return -EOPNOTSUPP;
> > > >>
> > > >> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> > > >> + wol.cmd = ETHTOOL_GWOL;
> > > >> dev->ethtool_ops->get_wol(dev, &wol);
> > > >>
> > > >> if (copy_to_user(useraddr, &wol, sizeof(wol)))
> > > >
> > > > It seems likely there are more of these.
> > > >
> > > > Is there any way for coccinelle to find them?
> > >
> > > Just curios: is static struct initialization (on stack) something that
> > > should be avoided ? I've been under the impression that static
> > > initialization allows thinner code and gives the compiler better chance
> > > for optimizations.
> >
> > Not in general. The (potential) problem here is that the structure has
> > padding and it is as a whole (i.e. including the padding) copied to
> > userspace. While I'm not aware of a compiler that wouldn't actually
> > initialize the whole data block including the padding in this case, the
> > C standard provides no guarantee about that so that to be sure we cannot
> > leak leftover kernel data to userspace, we need to explicitly initialize
> > the whole block.
>
> GCC will not always initialize the struct holes. This patch fixes a
> real bug that GCC on my system (v7.4)

Just checked (again) to be sure. No matter if the function is inlined or
not, gcc 7.4.1 initializes the structure by one movl (of 0x5) and two
movq (of 0x0), i.e. initializes all sizeof(struct ethtool_wolinfo) = 20
bytes including the padding.

One could certainly construct examples where a real life compiler would
only initialize the fields. That's why I said "in this case".

Michal Kubecek


2019-11-21 20:42:32

by Julia Lawall

[permalink] [raw]
Subject: Re: [Cocci] [PATCH] net: Zeroing the structure ethtool_wolinfo in ethtool_get_wol()



On Thu, 21 Nov 2019, Michal Kubecek wrote:

> On Thu, Nov 21, 2019 at 03:07:33PM +0300, Dan Carpenter wrote:
> > On Thu, Nov 21, 2019 at 12:19:17PM +0100, Michal Kubecek wrote:
> > > On Thu, Nov 21, 2019 at 11:23:34AM +0100, Enrico Weigelt, metux IT consult wrote:
> > > > On 26.10.19 21:40, Joe Perches wrote:
> > > > > On Sat, 2019-10-26 at 15:54 +0800, zhanglin wrote:
> > > > >> memset() the structure ethtool_wolinfo that has padded bytes
> > > > >> but the padded bytes have not been zeroed out.
> > > > > []
> > > > >> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
> > > > > []
> > > > >> @@ -1471,11 +1471,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
> > > > >>
> > > > >> static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
> > > > >> {
> > > > >> - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
> > > > >> + struct ethtool_wolinfo wol;
> > > > >>
> > > > >> if (!dev->ethtool_ops->get_wol)
> > > > >> return -EOPNOTSUPP;
> > > > >>
> > > > >> + memset(&wol, 0, sizeof(struct ethtool_wolinfo));
> > > > >> + wol.cmd = ETHTOOL_GWOL;
> > > > >> dev->ethtool_ops->get_wol(dev, &wol);
> > > > >>
> > > > >> if (copy_to_user(useraddr, &wol, sizeof(wol)))
> > > > >
> > > > > It seems likely there are more of these.
> > > > >
> > > > > Is there any way for coccinelle to find them?
> > > >
> > > > Just curios: is static struct initialization (on stack) something that
> > > > should be avoided ? I've been under the impression that static
> > > > initialization allows thinner code and gives the compiler better chance
> > > > for optimizations.
> > >
> > > Not in general. The (potential) problem here is that the structure has
> > > padding and it is as a whole (i.e. including the padding) copied to
> > > userspace. While I'm not aware of a compiler that wouldn't actually
> > > initialize the whole data block including the padding in this case, the
> > > C standard provides no guarantee about that so that to be sure we cannot
> > > leak leftover kernel data to userspace, we need to explicitly initialize
> > > the whole block.
> >
> > GCC will not always initialize the struct holes. This patch fixes a
> > real bug that GCC on my system (v7.4)
>
> Just checked (again) to be sure. No matter if the function is inlined or
> not, gcc 7.4.1 initializes the structure by one movl (of 0x5) and two
> movq (of 0x0), i.e. initializes all sizeof(struct ethtool_wolinfo) = 20
> bytes including the padding.
>
> One could certainly construct examples where a real life compiler would
> only initialize the fields. That's why I said "in this case".

Looking again at the case that I mentioned, I see:

# drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c:691: struct drm_amdgpu_info_device dev_info = {};
call __sanitizer_cov_trace_pc #
leaq 840(%rsp), %rdi #, tmp1126
xorl %eax, %eax # tmp1127
movl $46, %ecx #, tmp1128
rep stosq

So I guess the rep stosq is doing the memset.

julia

>
> Michal Kubecek
>
>
> _______________________________________________
> Cocci mailing list
> [email protected]
> https://systeme.lip6.fr/mailman/listinfo/cocci
>