LWN.net Logo

Object-oriented design patterns in the kernel, part 1

Object-oriented design patterns in the kernel, part 1

Posted Jun 1, 2011 16:58 UTC (Wed) by hummassa (subscriber, #307)
In reply to: Object-oriented design patterns in the kernel, part 1 by nbourdau
Parent article: Object-oriented design patterns in the kernel, part 1

[citation needed]


(Log in to post comments)

Object-oriented design patterns in the kernel, part 1

Posted Jun 1, 2011 18:31 UTC (Wed) by juliank (subscriber, #45896) [Link]

WG14/N1256 Committee Draft — Septermber 7, 2007 ISO/IEC 9899:TC3

6.7.8 Initialization
19 The initialization shall occur in initializer list order,each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

Object-oriented design patterns in the kernel, part 1

Posted Jun 1, 2011 20:15 UTC (Wed) by arjan (subscriber, #36785) [Link]

one gotcha; if your structure contains padding (due to alignment requirements of variable types).... it's not uncommon that this padding does not get initialized. In userspace this is often not a big deal.. but if you get this in the kernel you may accidentally leak kernel stack data to userspace....

Object-oriented design patterns in the kernel, part 1

Posted Jun 1, 2011 21:15 UTC (Wed) by pr1268 (subscriber, #24648) [Link]

Interesting... Just curious, has this been observed thus far? If so, has it been addressed (patch, git commit, etc.)?

Based on the standard, I'm unsure whether the padding you mention needs to be initialized. Assuming it does, then is the leaked stack data a compiler bug? Or, if it doesn't, then does this fall into the category of "making the compiler more efficient"?

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 0:26 UTC (Thu) by elanthis (guest, #6227) [Link]

I don't have a link, but it's a real problem that's been observed in the wild.

It's not a compiler bug by any means. It's correct, normal, expected behavior. There's no reason to initialize padding space in most circumstances, and doing so is just a loss of efficiency for no gain for those overwhelmingly common cases.

The only cases where this ever comes up as a problem are in security contexts (e.g. kernel stack data leakage) and when you're trying to do "undefined" operations like hashing a chunk of memory representing a struct. In the former case, you should be more careful and explicitly zero out memory via memset or calloc (or equivalent APIs) which you need to do anything for just about every other data structure you use so it's not a big deal, and in the later case you should be beaten with a stick until you stop doing such stupid things. :)

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 0:39 UTC (Thu) by foom (subscriber, #14868) [Link]

I ran into it when writing in-memory structs to disk (to be mmap'd in later). It was a problem that there was random garbage in between the data, since then it was not possible to md5sum two files and tell if they were the same.

Furthermore, gcc emits *terrible* code when explicitly initializing a bunch of integer members to 0 (especially where some of them are bitfields). My solution was to make a parent class call bzero to clear out sizeof(*this), and not explicitly initialize any of the initially-zero integer members. Made faster code *and* didn't have any padding issues. (But now someone's probably going to tell me that doing that is undefined and that gcc 4.8's optimizer is going to decide to transform my app into nethack, because I invoked some undefined behavior).

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 13:48 UTC (Thu) by cesarb (subscriber, #6266) [Link]

I believe that, unless your class has only plain old data members (the kind you would find in a C struct), doing a bzero of *this has the potential of blowing up. The compiler can add extra hidden members (like the vtable pointer), and you would be overwriting them.

That is, I believe what you are doing is safe only if:

- All the integer and bitfield members (and only them) are in the parent class;
- The parent class is zeroing only itself (that is, it is doing the sizeof(*this) itself, instead of being passed that value by the child class);
- The parent class has no virtual member functions or anything else that could make the compiler add C++ magic to it.

That said, I am no language lawyer, and would not be surprised if several clauses scattered all over the standard combine to say that even then you are still doing something undefined.

Why not move all these variables into a plain C-style struct, add it to your object as a member, and zero it on the constructor? That way sounds much safer to me, and I doubt the C++ standard would break it (since breaking it would break compatibility with C).

Object-oriented design patterns in the kernel, part 1

Posted Jun 3, 2011 11:20 UTC (Fri) by liljencrantz (guest, #28458) [Link]

What makes you assume the parent is talking C++ and not C? Am I missing something or are you?

Object-oriented design patterns in the kernel, part 1

Posted Jun 3, 2011 13:51 UTC (Fri) by sethml (subscriber, #8471) [Link]

Presumably because *this means something special in C++, and is not a common thing to write in C.

I've written this bug in C++ (memset(this, 0, sizeof(*this))) - clobbering your vtable pointer is pretty annoying to debug. My horrible hacky fix was just to zero the data portion of the class: memset(&firstMember, 0, (char *)(&lastMember + 1) - (char *)&firstMember)

Object-oriented design patterns in the kernel, part 1

Posted Jun 4, 2011 10:11 UTC (Sat) by liljencrantz (guest, #28458) [Link]

I always use the name «this» for the object pointer when doing OOP in C. From what I've seen, this is a common convention.

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 17:49 UTC (Thu) by marcH (subscriber, #57642) [Link]

> It's not a compiler bug by any means. It's correct, normal, expected behavior. There's no reason to initialize padding space in most circumstances, and doing so is just a loss of efficiency for no gain for those overwhelmingly common cases.

Performance over security: a very important design choice of C, one to keep in mind at all times. One carried over to C++. One responsible for zillions of security flaws written by thousands of programmers who should rather have used another, safer language (or stayed in bed).

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 9:25 UTC (Thu) by juliank (subscriber, #45896) [Link]

The standard (or my draft) says, in 6.2.6 Representations of types, 6.2.6.1 General:
6 When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 17:49 UTC (Thu) by jwakely (subscriber, #60262) [Link]

See http://lwn.net/Articles/417989/ and the mail it links to

> Check commit 1c40be12f7d8ca1d387510d39787b12e512a7ce8 for an example
> (net sched: fix some kernel memory leaks)

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 13:05 UTC (Thu) by pixelbeat (guest, #7440) [Link]

I think the standard says the whole object is initialized as it would be in static storage.
I.E. the padding is zeroed too.

I previously did some analysis to verify.

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 13:08 UTC (Thu) by juliank (subscriber, #45896) [Link]

> I.E. the padding is zeroed too.
It's not, read my other comment.

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 14:30 UTC (Thu) by pixelbeat (guest, #7440) [Link]

Well by my analysis in the above link, GCC does init the padding to zero. I.E. it seems GCC interprets 6.7.8 to trump 6.2.6.
I'd be very interested if there was a counter example in the wild

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 16:19 UTC (Thu) by juliank (subscriber, #45896) [Link]

> Well by my analysis in the above link, GCC does init the padding to zero.
Did you actually enable optimizations when testing?

> I'd be very interested if there was a counter example in the wild

https://lwn.net/Articles/417989/
https://lwn.net/Articles/417994/

Object-oriented design patterns in the kernel, part 1

Posted Jun 3, 2011 1:59 UTC (Fri) by pixelbeat (guest, #7440) [Link]

> Did you actually enable optimizations when testing?
That is immaterial as optimizations may zero the padding.
The interesting case to test is the non optimized case.

> https://lwn.net/Articles/417989/
> https://lwn.net/Articles/417994/
They concur with my testing I think.
I.E. only in the case where all members are specified
in the initializer list, is the padding not zeroed.

Object-oriented design patterns in the kernel, part 1

Posted Jun 2, 2011 18:00 UTC (Thu) by nix (subscriber, #2304) [Link]

Well, you could check the code, or (more practically) GCC's intermediate representations, both of which make it fairly clear that GCC emits initializations for every field independently, then subjects them all to optimization just as if you'd written them as separate statements. Sometimes (especially for large structures) this may lead to initializations being merged and blatting over gaps: very often, it does not.

Copyright © 2013, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds