|
|
Subscribe / Log in / New account

File permissions in the kernel

There are many ways to make a poor impression in the kernel community; Baole Ni surely stumbled across one of them: post 1,285 separate cleanup patches, each with the same subject line, and each copied to a long list of developers. It was, David Miller said, "one of the worst patch series submissions in history." In theory, the objective of the patch was reasonable: replace hard-coded constants with their symbolic equivalents. But, it seems, this is a case where the community would rather see the numbers directly.

The change in question relates to places in the kernel where file permissions are specified — usually permissions for files to be created in sysfs or /proc. There is a set of macros defined in <linux/stat.h> for these permissions bits, but it is common practice in the kernel — and among users of Unix-like systems in general — to just use their octal equivalent instead. Thus, for example, one will often see 0444 instead of S_IRUGO. Indeed, it seems one will see it at least 1,285 times, given the length of the patch set sent to eliminate octal permissions from the kernel.

There were obviously lots of complaints about how the patch set was done, but there was also a lot of opposition to the change itself. It seems that many people find a string like 0644 easier to read than S_IWUSR|S_IRUGO. In the end, Linus made that approach official, saying that he did not want to see any of the cleanup patches merged and that, in fact, it would be better to convert users of the macros to octal strings instead.

Octal constants are not perfect either; as Al Viro pointed out, they are subject to subtle and hard-to-see errors. Perhaps, it was suggested, the real problem is that the (POSIX-defined) S_* macros are hard to read, obscuring the developer's intent rather than clarifying it. As an alternative, Ingo Molnar has proposed the adoption of a new set of macros, defined like this:

    #define PERM_rw_______	0600
    #define PERM_rw_r_____	0640
    #define PERM_rw_r__r__	0644
    #define PERM_rw_rw_r__	0664
    #define PERM_rw_rw_rw_	0666

All of the "useful" combinations have macros defined, while there are none for settings that don't make sense. Use of these macros, he said, would make the code clearer and make it harder to introduce security problems. Actually getting them merged, though, might require overcoming the habits of developers who have been typing octal constants for decades. The eventual discussion could yet end up being longer than the patch series that provoked it.


to post comments

File permissions in the kernel

Posted Aug 4, 2016 7:59 UTC (Thu) by NRArnot (subscriber, #3033) [Link] (1 responses)

#define PERM_rw_______ 0600

Yuk! How many trailing underscores, and how do they aid comprehension?

I'd suggest PERM_rw_0_0, with 0 standing for no bits set. But I prefer 0600 raw.

File permissions in the kernel

Posted Aug 4, 2016 14:42 UTC (Thu) by mathstuf (subscriber, #69389) [Link]

Is that because permission bits are the only thing still using octal today? If they were hex, there are plenty of other "magic" hex literals out there to cause confusion as to what a specific hex literal might mean (particularly when assigned away from its usage for some bitflips). While the underscore train is annoying, I personally try to avoid "magic constants", even permission bits and instead use the S_* macros and even things like STDIN_FILENO and friends.

File permissions in the kernel

Posted Aug 4, 2016 15:25 UTC (Thu) by epa (subscriber, #39769) [Link] (15 responses)

Is there a reason these are done as #define instead of const int?

File permissions in the kernel

Posted Aug 4, 2016 19:51 UTC (Thu) by k8to (guest, #15413) [Link] (14 responses)

There are reasons to prefer defines over const vars. A const int with global visibility occupies memory, while a define may not (it may become an operation immediate). There's also some readability value to the namespacing and practice of representing such symolic constant values consistently instead of picking different approaches depending upon the values. I'm sure there are more.

File permissions in the kernel

Posted Aug 5, 2016 5:47 UTC (Fri) by epa (subscriber, #39769) [Link] (9 responses)

Does a const int included via a header file occupy any memory? I assumed it would be inlined whenever used.

File permissions in the kernel

Posted Aug 5, 2016 6:35 UTC (Fri) by johill (subscriber, #25196) [Link] (6 responses)

It would consume memory in every C file that includes it and even break the link unless it was declared static.

File permissions in the kernel

Posted Aug 5, 2016 6:38 UTC (Fri) by johill (subscriber, #25196) [Link] (5 responses)

Oh, wait - const. That doesn't seem to be true then, sorry.

File permissions in the kernel

Posted Aug 5, 2016 6:41 UTC (Fri) by johill (subscriber, #25196) [Link] (4 responses)

Hmm, but looking further, that seems only due to the optimiser, and in fact without the static it does cause build errors. So it's not clear to me whether or not it'll be actually put into every file that doesn't use it, but if you *do* use it then it will consume memory.

File permissions in the kernel

Posted Aug 5, 2016 19:39 UTC (Fri) by xtifr (guest, #143) [Link] (3 responses)

This is one of the subtle differences between C and C++. Stroustrup didn't like the preprocessor, so he declared that a const of a fundamental type (at least) would be inherently inline. It can even participate in constant folding at compile time. He did this specifically so that you could declare consts instead of #defines. C didn't go this route.

In C, if you put "const int foo = 5;" in a header, and include it in multiple files, you'll get a link-time error about multiple definitions. In C++, this not only works, it's preferred.

This is something that can easily trip up a C++ programmer who isn't used to C. C++ books tend to emphasize that #defines are bad and you should use consts instead.

I'm guessing that epa works with C++ more often than with C. :)

File permissions in the kernel

Posted Aug 5, 2016 23:47 UTC (Fri) by Jonno (subscriber, #49613) [Link] (2 responses)

Actually, the only difference between C and C++ in this regards is that in C++, const in the global scope implies static, while in C it doesn't. So C++ "const int = 5;" is equivalent to C and C++ "static const int = 5;", while C "const int = 5" is equivalent to C++ "extern const int = 5;" (which is invalid syntax in standard C, as in C extern can only be used in a declaration and not in a definition). In both C and C++ you would use "extern const int;" to access the non-static constant from a different compilation unit.

File permissions in the kernel

Posted Aug 6, 2016 19:16 UTC (Sat) by xtifr (guest, #143) [Link]

Really? I haven't actually looked that closely.

The difference may be less than you think, though. You can make a pointer to a const int (and this should prevent the compiler from optimizing away the storage), but the same is true of an inline function. The real difference between an inline and a static is that a static will have a separate copy for each translation unit, while an inline will only have one copy for the whole program. (Assuming they aren't all optimized away.)

For normal use (no pointers), the difference won't matter, since the storage will be elided if you turn on even the most basic optimizations (which is pretty normal). And, since you can't modify a const int even with a cast (unless I've just found a bug in g++), the only practical difference would be if you tried to compare two different pointers to what you thought was the same const int. If the const is static, then the comparison *might* fail; if it's inline, it would succeed. But that's not something I've ever done or seen done, so I honestly don't know what the right answer is.

Anyway, I suspect we're drifting a bit off topic, and what you say sounds plausible enough, so I'm certainly not going to contradict you.

File permissions in the kernel

Posted Aug 13, 2016 2:38 UTC (Sat) by thestinger (guest, #91827) [Link]

There are differences. C++ allows a static const to be used in a constant expression, such as defining another static const or using it as a non-variable-length array size.

File permissions in the kernel

Posted Aug 5, 2016 7:11 UTC (Fri) by k8to (guest, #15413) [Link] (1 responses)

In the C model, each code unit (file) is separately compiled into objects, and the system presumes all global symbols are globally visible. When a global const int is compiled into a code unit, memory is set aside for it, and the generated code uses the value by a memory offset.

Theoretically, in another language, the promise that it is const could be strong enough that whole-program optimization could eventually fold away its existence as a memory-located value, but C doesn't give promises that strong for const items.

Placing the definition in a header doesn't help, then you get N definitions of the same-named item, all with global scope. I'm pretty sure this is an error, and in any event if it worked, you'd have the value in memory many times, once for each unit.

Declaring it extern in a header and providing it in one unit gets you one value in memory, but still you don't get the immediates. The meaning of such a symbol is a memory value.

If you declare it as static const int, then a relatively modern compiler will *usually* be able to fully optimize it. You can still break everything by taking the address of it anywhere, which will push it back into memory. Sometimes insufficiently experienced programmers will make this type of mistake, and de-optimize your carefully constructed const int, potentially causing problems later that can be difficult to spot.

The define is very simple to understand, by contrast, which is it's strength.

File permissions in the kernel

Posted Aug 5, 2016 13:32 UTC (Fri) by epa (subscriber, #39769) [Link]

You're right, static const int is what's needed. But just taking the address of it, while it does require allocating four bytes of space for the integer, need not stop other uses of it from being an inlined constant. A quick experiment with gcc -O seems to confirm this.

The arguments for and against use of the preprocessor have often been covered. One common reason to prefer language constructs over macros is that they are easier for static analysis tools. I wonder whether the Sparse tool is able to track uses of a named constant defined with the preprocessor?

File permissions in the kernel

Posted Aug 5, 2016 7:27 UTC (Fri) by mchapman (subscriber, #66589) [Link] (2 responses)

> There are reasons to prefer defines over const vars. A const int with global visibility occupies memory, while a define may not (it may become an operation immediate).

I have frequently seen enum values used for constants instead of preprocessor macros or const variables. They end up in the program's debugging info, so you can use their names in gdb, but they don't require any memory in the process itself.

File permissions in the kernel

Posted Aug 5, 2016 13:55 UTC (Fri) by andresfreund (subscriber, #69562) [Link]

Fwiw, GCC/gdb can do that with macros too. Quite useful. -ggdb seems to do the trick for me.

File permissions in the kernel

Posted Aug 8, 2016 1:39 UTC (Mon) by JdGordy (subscriber, #70103) [Link]

For some reason MISRA (or the subset I'm using at work) forbids #define's so we are stuck using the enum version.

File permissions in the kernel

Posted Aug 5, 2016 13:30 UTC (Fri) by Jonno (subscriber, #49613) [Link]

> A const int with global visibility occupies memory
But a static const int does not (assuming it's initializer is a constexpr, it's address is never taken, and optimizations are turned on or --fno-keep-static-consts is passed to gcc).

File permissions in the kernel

Posted Aug 4, 2016 16:27 UTC (Thu) by error27 (subscriber, #8346) [Link]

I have an unreleased Smatch warning that would have caught Al Viro's bug. The heuristic is you're not allowed to do a bitwise OR or AND with a decimal greater than 10. (Unless it is a single solid block of set bits, like 0xf0 in decimal. I have no idea why people would do that but apparently they must or I wouldn't have written it).

The problem is that you have some false positives where people do "cmd = SET_TIMER | 10;" where 10 is the number of seconds. I have found a few bugs with this check but I haven't pushed it because of the false positives. Anyway, if anyone had introduced Al Viro's bug then I would probably have emailed them.

File permissions in the kernel

Posted Aug 16, 2016 14:53 UTC (Tue) by marcH (subscriber, #57642) [Link]

> Actually getting them merged, though, might require overcoming the habits of developers who have been typing octal constants for decades.

I've met many people who wrongly believe that octal can do everything and who don't realize that chmod can perform incremental changes like this - without any octal equivalent whatsoever:

chmod -R g=r foo/

I think I've even seen at times attempts to re-implement commands like the above with a script...


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