LWN.net Logo

Remote root hole in Samba

Remote root hole in Samba

Posted Apr 12, 2012 10:42 UTC (Thu) by epa (subscriber, #39769)
In reply to: Remote root hole in Samba by lopgok
Parent article: Remote root hole in Samba

Well, whatever the arguments about using C or C++ for new projects (and I don't want to get into a language war) there is certainly a lot of existing code in these languages.

My point is that bounds checking is not something which requires changing your programming language. It can be added by the compiler when compiling C or C++ code while remaining entirely faithful to the language standards for those languages.


(Log in to post comments)

Remote root hole in Samba

Posted Apr 12, 2012 18:34 UTC (Thu) by endecotp (guest, #36428) [Link]

> [bounds checking] can be added by the compiler when compiling
> C or C++ code while remaining entirely faithful to the language
> standards for those languages.

No it can't; consider this:

int f(const int* ptr, size_t offset)
{
return ptr[offset];
}

in another translation unit:

int g()
{
int a[100];
return f(a,100);
}

You can't add bounds checking in f() because it doesn't know the size of the array that ptr points to. You can't add bounds checking in g() because it doesn't know that 100 is being used as an array index.

You can try to carry around the valid bounds of every pointer by making each pointer actually 3 addresses. You might get quite a long way with that approach, but eventually you'll hit something like casting to/from integers that breaks it.

Remote root hole in Samba

Posted Apr 12, 2012 20:16 UTC (Thu) by khim (subscriber, #9252) [Link]

You can't add bounds checking in f() because it doesn't know the size of the array that ptr points to.

Well, with the existing ABI it does not know, but you can change the ABI. As I've said: it was done before. Actually guys from MCST had hardware support, but it can be done entirely programmatically, too - just with bigger overhead.

You might get quite a long way with that approach, but eventually you'll hit something like casting to/from integers that breaks it.

All such casts conveniently trigger undefined behavior thus are not guaranteed to work. Unions are especially nasty (this is where MCST guys needed hardware support to keep everything working safely and speedily), but yes, you can write entirely safe ISO C language if you want. Of course it's free(3) will probably be noop (or, alternatively malloc(3) can always return NULL) because you'll need to track all allocations, but it's doable.

Remote root hole in Samba

Posted Apr 12, 2012 20:27 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

As I understand, MCST guys have NOT made a safe hardware. They've just implemented pointer tagging. It's more than possible to have unsafe operations there.

For example:
int some_array[10];
int some;
int permissions[3];

some_array[12]=0xFFFF;

It's all legal - we're not writing into uninitialized RAM and we're not casting pointers to ints.

Besides, E2k is vaporware anyway.

Remote root hole in Samba

Posted Apr 12, 2012 20:39 UTC (Thu) by khim (subscriber, #9252) [Link]

As I understand, MCST guys have NOT made a safe hardware.

Yes, they did.

It's all legal - we're not writing into uninitialized RAM and we're not casting pointers to ints.

No, it's illegal. They've used fat pointers thus some_array pointer is pointer with some base address and limit equal to 10. It's illegal to access 12th element using it.

The whole OS was supposed to live in one huge flat address space thus it was important not only to distinguish pointers from non-pointers but also to distinguish “your pointers” from “someone else's pointers”. This is impossible to do without boundary checking.

Besides, E2k is vaporware anyway.

Well, YMMV. The last time they visited our office they had chip but no chipset. Motherboard with tiny cheap CPU and three huge expensive FPGAs (with chipset) is not exactly something you can sell, but it's real in some sense.

Remote root hole in Samba

Posted Apr 12, 2012 21:46 UTC (Thu) by endecotp (guest, #36428) [Link]

> All such casts conveniently trigger undefined behavior
> thus are not guaranteed to work

My recollection is that you're permitted to cast e.g. a char* to an intptr_t and back again, and expect to get the same pointer that you started with.

Remote root hole in Samba

Posted Apr 12, 2012 22:51 UTC (Thu) by nybble41 (subscriber, #55106) [Link]

While that seems to be the intent, and the most common implementation, all I could find on the subject in the C99 draft standard is this:

> An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

> Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

So integer-pointer conversions either way are implementation-defined, and pointer-to-integer conversions invoke undefined behavior if the pointer cannot be represented as an integer, which would probably be the case for an implementation with large, bound-checked pointers.

Remote root hole in Samba

Posted Apr 12, 2012 22:59 UTC (Thu) by khim (subscriber, #9252) [Link]

While that seems to be the intent

This was never the intent. Remember the first C standard (ANSI C 89 or ISO 90) was developed when things like iAPX 432 were supposed to be the future. Sure, this direction died off and we've got IA-32 and PPC instead, but language was defined to be usable on such architectures and in such systems. I suspect this is important for things like Alchemy today.

Remote root hole in Samba

Posted Apr 12, 2012 23:53 UTC (Thu) by nybble41 (subscriber, #55106) [Link]

There is a footnote in the C99 draft standard to the effect that the intent was for the conversions to reflect the underlying machine architecture. Not a requirement, of course--just a suggestion.

It's an application of the "principle of least surprise". On a machine with uniform, byte-addressable memory (unlike the iAPX 432), the least surprising integer representation of a pointer is the object's byte address, which can easily be converted in both directions without any loss of information.

The standard does specify that you can convert any pointer to a char* and access the object one byte at a time. I'm not sure how one would reconcile that with a type-checked architecture like the iAPX 432.

Remote root hole in Samba

Posted Apr 12, 2012 22:52 UTC (Thu) by khim (subscriber, #9252) [Link]

My recollection is that you're permitted to cast e.g. a char* to an intptr_t

Yes.

and back again

No. This is forbidden. There is one exception: you can assign null pointer constant (which is an integer constant expression with the value 0, or such an expression cast to type void *) to pointer. Yes, zero is special even in C11, not just in C++11! Other integers can not be assigned. Or rather: they can but the result is undefined behavior (as usual in C).

This may be surprising to you but C does not require flat memory model and it does not guarantee the ability to create pointer from just some random sequence of bits.

Remote root hole in Samba

Posted Apr 13, 2012 2:29 UTC (Fri) by mmorrow (subscriber, #83845) [Link]

>> My recollection is that you're permitted to cast e.g. a char* to an intptr_t

> Yes.

>> and back again

> No. This is forbidden. There is one exception: you can assign null pointer constant...to pointer.
> ...C11 ...
> Other integers can not be assigned. Or rather: they can but the result is undefined behavior (as usual in C).

This is not forbidden by ISO C11.

[7.20.1.4.1] states:

"[intptr_t] designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer[.]"

It seems that you are taking [6.5.16.1] (second to last item):

"the left operand is [a]...pointer, and the right is a null pointer constant..."

to mean that this is the *only* way that a pointer can be assigned an integral value (constant or otherwise). However, consider [6.5.4.3] (emphasis added):

"Conversions that involve pointers, *other that where permitted by the constraints of 6.5.16.1*, shall be specified by means of an explicit cast."

So, the null pointer constant is just the only integral value you can assign to a pointer *without an explicit cast*.

(By pure coincidence the C11 spec happens to be on my desk at arms length ;)

Remote root hole in Samba

Posted Apr 13, 2012 4:18 UTC (Fri) by khim (subscriber, #9252) [Link]

This is not forbidden by ISO C11.

[7.20.1.4.1] states:
"[intptr_t] designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer[.]"

You are correct, of course. My bad. If intptr_t exist in a given implementation then you must be able to do such conversion - but it's clearly optional as noted in the very same 7.20.1.4.1 paragraph.

Remote root hole in Samba

Posted Apr 13, 2012 22:42 UTC (Fri) by mmorrow (subscriber, #83845) [Link]

True, supporting {,u}intptr_t is optional. Though, (void*)(int) is still allowed. Consider:

int x;
void *p;
x = ..;
assert(x);
p = (void*)x;
assert(p==NULL); /*only possibility for a fat-pointer implem?*/

It seems to me that a in conforming implementation which uses fat-pointers, the only possible way the above could be implemented is to implement all such (void*)(int) casts as resulting in NULL. Whether this is permissible I'm not certain.

Remote root hole in Samba

Posted Apr 13, 2012 22:49 UTC (Fri) by mmorrow (subscriber, #83845) [Link]

(or replace "p==NULL" with "undefined behaviour", but the question remains "Is this permissible?")

Remote root hole in Samba

Posted Apr 14, 2012 12:17 UTC (Sat) by khim (subscriber, #9252) [Link]

It seems to me that a in conforming implementation which uses fat-pointers, the only possible way the above could be implemented is to implement all such (void*)(int) casts as resulting in NULL.

Not necessarily. 0 must be converted to NULL, but it's quite Ok to produce different pointers for different numbers. The simple way will be to just store given integer in the “address” part of pointer and store zero in “width” part of pointer. This will give you different yet equally unusable pointers.

Remote root hole in Samba

Posted Apr 14, 2012 14:20 UTC (Sat) by mmorrow (subscriber, #83845) [Link]

Ah true, fat-pointers to zero-width memory blocks do appear to give a way to map "int" into "(void*)" in a controlled way.

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