Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Wed, 24 Nov 2010 09:52:47 -0500
From: Dan Rosenberg <dan.j.rosenberg@...il.com>
To: oss-security@...ts.openwall.com
Subject: Interesting behavior with struct initiailization

This topic has come up a few times recently on lkml, so I thought I'd
share my findings here for the sake of spreading information.

Lately, there have been a high number of instances in the Linux kernel
where uninitialized stack bytes are leaked to unprivileged users as a
result of copying structures to userland.  There's been recent
discussion on the proper way to make sure this doesn't happen.

There are three situations in which this might happen:

===============================

1. Lack of initialization

This is the easiest to spot.  For example:

---
struct test { int a; int b; int c; } arg;

arg.a = 0;
arg.b = 0;

copy_to_user(ptr, &arg, sizeof(arg));
---

The contents of arg.c will be leaked due to lack of initialization.
This is known and expected behavior.

===============================

2. Lack of initialization of padding bytes

gcc adds padding bytes to some structures to give them more natural
alignment.  If these bytes aren't cleared using memset() or C99
initialization (more on this soon), they'll be uninitialized and
subsequently leaked:

---
struct test { int a; char b; int c; } arg;

arg.a = 0;
arg.b = 0;
arg.c = 0;

copy_to_user(ptr, &arg, sizeof(arg));
---

The three bytes padding after the "char b" member will remain
uninitialized and are leaked in this example.

===============================

3. gcc does not clear padding bytes on full C99 initialization

I think this is unexpected behavior (at least to me), and it's the
reason I'm writing this post.  Normally, C99 initialization
automatically zeros out padding bytes as well.  For example:

---
struct test { int a; char b; int c; } arg = {};

or

struct test { int a; char b; int c; } arg = { .a = 1 };
---

will set the specified fields, and zero out everything else, including
padding bytes.  However, if you explicitly initialize every member
using C99 initialization, the padding bytes won't be zeroed out:

---
struct test { int a; char b; int c; } arg = { .a = 0, .b = 0, .c = 0 };
---

This will leave the padding bytes after "char b" uninitialized,
surprisingly.  I imagine this is an attempted optimization on gcc, but
now it's coming back to bite (no pun intended) everyone who relied on
this construct to prevent leakage.

Regards,
Dan

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.