Discussion:
Stack Clash affecting mostly Unix-ish
(too old to reply)
Noob
2017-06-22 14:41:37 UTC
Permalink
Hello,

I suppose most of you have been aware of the "stack clash"
variant on stack smashing.

https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
https://news.ycombinator.com/item?id=14587745

One thing that I found perplexing is that the issue affects
a broad range of Unixy OSes, and not Windows. What is it that
Windows did that other vendors didn't?

I suppose Windows implements guard pages?
(That seems to be necessary but not sufficient.)

And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)

However, one may use gcc variants to compile software
for Windows. These binaries would be vulnerable, right?

Regards.
Scott Lurndal
2017-06-22 15:32:52 UTC
Permalink
Post by Noob
Hello,
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
Note that this only applies to 32-bit linux. 64-bit linux
has significantly more space between stack and heap and
and has non-heap segments between the stack and heap preventing
the stack from growing into the heap.

000001152000-000001332000 rw-p 00000000 00:00 0 [heap]
7fffbbe95000-7fffbbeaa000 rw-p 00000000 00:00 0 [stack]

_All_ the linux systems I've used, worked with or worked on in the last
decade have been 64-bit, so I'm not sure how many actual systems are
in play for this bug.
Noob
2017-06-22 16:09:18 UTC
Permalink
Post by Scott Lurndal
Post by Noob
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
Note that this only applies to 32-bit linux. 64-bit linux
has significantly more space between stack and heap and
and has non-heap segments between the stack and heap preventing
the stack from growing into the heap.
000001152000-000001332000 rw-p 00000000 00:00 0 [heap]
7fffbbe95000-7fffbbeaa000 rw-p 00000000 00:00 0 [stack]
_All_ the linux systems I've used, worked with or worked on in the last
decade have been 64-bit, so I'm not sure how many actual systems are
in play for this bug.
See IV.1.7. 64-bit exploitation
Post by Scott Lurndal
The address-space of a 64-bit process is so vast that we initially
thought it was impossible to clash the stack with another memory region;
we were wrong.
See also https://access.redhat.com/security/cve/CVE-2017-1000379

I wonder if 32-bit and 64-bit variants of Windows have the same
status wrt this vuln.

Regards.
Scott Lurndal
2017-06-22 16:28:58 UTC
Permalink
Post by Noob
Post by Scott Lurndal
Post by Noob
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
Note that this only applies to 32-bit linux. 64-bit linux
has significantly more space between stack and heap and
and has non-heap segments between the stack and heap preventing
the stack from growing into the heap.
000001152000-000001332000 rw-p 00000000 00:00 0 [heap]
7fffbbe95000-7fffbbeaa000 rw-p 00000000 00:00 0 [stack]
_All_ the linux systems I've used, worked with or worked on in the last
decade have been 64-bit, so I'm not sure how many actual systems are
in play for this bug.
See IV.1.7. 64-bit exploitation
It appears to depend on kernel version. The Redhat6 kernels (2.6) aren't
vulnerable because the have the heap way below the stack. FC20 (3.19),
on the other hand, moved the heap up high and is vulnerable.
Kaz Kylheku
2017-06-23 00:49:48 UTC
Permalink
Post by Scott Lurndal
Post by Noob
Hello,
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
Note that this only applies to 32-bit linux. 64-bit linux
has significantly more space between stack and heap and
and has non-heap segments between the stack and heap preventing
the stack from growing into the heap.
How much space does 64 bit glibc ensure between thread stack mmaps?
Richard Kettlewell
2017-06-22 16:58:29 UTC
Permalink
Post by Noob
One thing that I found perplexing is that the issue affects
a broad range of Unixy OSes, and not Windows. What is it that
Windows did that other vendors didn't?
I suppose Windows implements guard pages?
(That seems to be necessary but not sufficient.)
It’s not sufficient if it’s possible to trigger a large enough
allocation to ‘jump over’ the guard page.
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That’s the ‘stack probe’ technique, which
http://seclists.org/oss-sec/2017/q2/502 says it has been doing since XP.
--
http://www.greenend.org.uk/rjk/
Noob
2017-06-22 20:18:06 UTC
Permalink
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That's the 'stack probe' technique, which
http://seclists.org/oss-sec/2017/q2/502
says it has been doing since XP.
It seems there needs to be some "cooperation" between
user-space and kernel to guard against the class of
vulns under discussion.

Kernel needs to enforce a "no tresspassing" gap
between stack and heap. (BTW, "the" stack is often
mentioned, but doesn't every thread get a stack?)

And user-space needs to make sure it will not jump
over the gap. Perhaps VS enforces that for the code
it compiles, but code compiled with mingw would have
the same vuln than on Linux, right?

Regards.
Rainer Weikusat
2017-06-22 20:55:38 UTC
Permalink
Post by Noob
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That's the 'stack probe' technique, which
http://seclists.org/oss-sec/2017/q2/502
says it has been doing since XP.
It seems there needs to be some "cooperation" between
user-space and kernel to guard against the class of
vulns under discussion.
Why didn't you make that "It seems there needs to be some coop bet uspac
and kern to guard ag the class of vulns un disc"? I mean, use of
polysyllables is clearly un-american as nobody has that much time on his
hands! [I mean, use of pols is clear un- as nob ...]

[Sorry for the OT-remark but I *hate* this language mutilation by "I
can't be arsed!"-people]
Post by Noob
Kernel needs to enforce a "no tresspassing" gap
between stack and heap. (BTW, "the" stack is often
mentioned, but doesn't every thread get a stack?)
And user-space needs to make sure it will not jump
over the gap. Perhaps VS enforces that for the code
it compiles, but code compiled with mingw would have
the same vuln than on Linux, right?
The vulnerability (vulnerabilities, actually) under discussion is that
various programs copy an arbitrary amount of data from untrusted sources
onto the stack despite running with elevated privileges.

The solution to this is to fix the code, not come up with yet another
bizarre exploit mitigation scheme ...
William Ahern
2017-06-23 19:05:01 UTC
Permalink
Post by Rainer Weikusat
Post by Noob
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That's the 'stack probe' technique, which
http://seclists.org/oss-sec/2017/q2/502
says it has been doing since XP.
It seems there needs to be some "cooperation" between
user-space and kernel to guard against the class of
vulns under discussion.
Why didn't you make that "It seems there needs to be some coop bet uspac
and kern to guard ag the class of vulns un disc"? I mean, use of
polysyllables is clearly un-american as nobody has that much time on his
hands! [I mean, use of pols is clear un- as nob ...]
[Sorry for the OT-remark but I *hate* this language mutilation by "I
can't be arsed!"-people]
Post by Noob
Kernel needs to enforce a "no tresspassing" gap
between stack and heap. (BTW, "the" stack is often
mentioned, but doesn't every thread get a stack?)
And user-space needs to make sure it will not jump
over the gap. Perhaps VS enforces that for the code
it compiles, but code compiled with mingw would have
the same vuln than on Linux, right?
The vulnerability (vulnerabilities, actually) under discussion is that
various programs copy an arbitrary amount of data from untrusted sources
onto the stack despite running with elevated privileges.
Some languages like C++ and Rust boast about how well (and how seamlessly)
they do dynamic allocation on the stack. AFAIU Rust will even try to
allocate closures and (soon) coroutines on the stack. (Given Rust's
ownership semantics, it's burdensome to write code that doesn't also happen
to lend itself to a stack-like, LIFO-ordered allocation pattern.)
Post by Rainer Weikusat
The solution to this is to fix the code, not come up with yet another
bizarre exploit mitigation scheme ...
The correct solution is compiler stack probes, pure and simple. In terms of
code injection it's not a mitigation, but a complete solution. There's no
contention on this point. (It's worth pointing out that PATH_MAX on Linux is
4096, the size of a VM page on both the x86 and amd64 architectures.[1]) The
maintainers of both GCC and LLVM have had this on their TODO list for a long
while, but it was low priority. This is one of those instances where an
obvious security issue was left unattended because people tend to think that
because they find it difficult to construct a realistic exploit, then others
must find it equally difficult.

I'm surprised stack probes hadn't been implemented yet. The last few times
I've counciled a C programmer not to use VLAs or alloca(), especially for
user-tainted data, I qualified the advice with the observation that stack
probes constrained the issue to merely segfaulting on malicious (or
maliciously stupid) input. I thought stack probes had been implemented a few
years ago as part of the support for segmented stacks (for Go on GCC and
Rust on LLVM). But apparently in both cases that stack probe work was
specific to the segmented stack support and not implemented in a more
generalize fashion.

The Rust team especially has egg on its face. When they moved away from
segmented stacks they appear to have _known_ that they were losing stack
probes. Rust makes hay about it's cost-free, guaranteed memory safety, as
well as it's robust use of stack allocation.


[1] Even a well-written component--e.g. a ftw(3) implementation--that
recursively walks a directory tree could be vulnerable. Or even code that
doesn't recurse but is merely invoked from a language like Rust or C++ that
heavily relies on stack allocation. IME, excessive use of dynamic allocation
in C often leads to bugs and exploits. GNU project code is notorious for its
complexity, and IMO part of that is the GNU style manual rule that there
should never be arbitrary size limits, which results in implementations that
rely on dynamic allocation for every non-integral object. Arbitrary limits
are great simplifiers. Arbitrary limits make stack allocation more natural
and more defensible, even in a language like C.
Jorgen Grahn
2017-06-23 19:44:58 UTC
Permalink
...
Post by William Ahern
Post by Rainer Weikusat
The vulnerability (vulnerabilities, actually) under discussion is that
various programs copy an arbitrary amount of data from untrusted sources
onto the stack despite running with elevated privileges.
Some languages like C++ and Rust boast about how well (and how seamlessly)
they do dynamic allocation on the stack.
Don't know about Rust, but with C++ it's under the programmer's
control. If I put the untrusted data in a std::vector or std::string,
it won't be on the stack, but perhaps managed by an object on the stack.

I'm not sure I know how to "copy an arbitrary amount of data ... onto
the stack" -- or how to make the stack grow uncontrolledly at all.
C99 variable-length arrays?

(Didn't read any of the sources. Sorry.)

/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
s***@potato.field
2017-06-26 08:37:15 UTC
Permalink
On 23 Jun 2017 19:44:58 GMT
Post by Jorgen Grahn
....
Post by William Ahern
Post by Rainer Weikusat
The vulnerability (vulnerabilities, actually) under discussion is that
various programs copy an arbitrary amount of data from untrusted sources
onto the stack despite running with elevated privileges.
Some languages like C++ and Rust boast about how well (and how seamlessly)
they do dynamic allocation on the stack.
Don't know about Rust, but with C++ it's under the programmer's
control. If I put the untrusted data in a std::vector or std::string,
it won't be on the stack, but perhaps managed by an object on the stack.
It could be on the stack if you pass your own allocator to the container.
Post by Jorgen Grahn
I'm not sure I know how to "copy an arbitrary amount of data ... onto
the stack" -- or how to make the stack grow uncontrolledly at all.
C99 variable-length arrays?
alloca()
--
Spud
Jorgen Grahn
2017-06-27 08:13:37 UTC
Permalink
Post by s***@potato.field
On 23 Jun 2017 19:44:58 GMT
Post by Jorgen Grahn
....
Post by William Ahern
Post by Rainer Weikusat
The vulnerability (vulnerabilities, actually) under discussion is that
various programs copy an arbitrary amount of data from untrusted sources
onto the stack despite running with elevated privileges.
Some languages like C++ and Rust boast about how well (and how seamlessly)
they do dynamic allocation on the stack.
Don't know about Rust, but with C++ it's under the programmer's
control. If I put the untrusted data in a std::vector or std::string,
it won't be on the stack, but perhaps managed by an object on the stack.
It could be on the stack if you pass your own allocator to the container.
True.
Post by s***@potato.field
Post by Jorgen Grahn
I'm not sure I know how to "copy an arbitrary amount of data ... onto
the stack" -- or how to make the stack grow uncontrolledly at all.
C99 variable-length arrays?
alloca()
And yes, that one too.

And yet another (perhaps obvious) one: recursion.

Still, these are all rather exotic, and I'd expect people to be aware
that you can't let untrusted data control how you use them. It's a
standard criticism against VLAs, for example.

/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
s***@potato.field
2017-06-27 08:46:52 UTC
Permalink
On 27 Jun 2017 08:13:37 GMT
Post by Jorgen Grahn
Post by William Ahern
Post by Jorgen Grahn
I'm not sure I know how to "copy an arbitrary amount of data ... onto
the stack" -- or how to make the stack grow uncontrolledly at all.
C99 variable-length arrays?
alloca()
And yes, that one too.
And yet another (perhaps obvious) one: recursion.
Still, these are all rather exotic, and I'd expect people to be aware
Recursion is hardly exotic. And alloca() is a good way to allocate small
amounts of local dynamic memory in a C function and not have to worry about
calling free() before returning.
--
Spud
Jorgen Grahn
2017-06-28 17:38:05 UTC
Permalink
Post by s***@potato.field
On 27 Jun 2017 08:13:37 GMT
Post by Jorgen Grahn
Post by William Ahern
Post by Jorgen Grahn
I'm not sure I know how to "copy an arbitrary amount of data ... onto
the stack" -- or how to make the stack grow uncontrolledly at all.
C99 variable-length arrays?
alloca()
And yes, that one too.
And yet another (perhaps obvious) one: recursion.
Still, these are all rather exotic, and I'd expect people to be aware
Recursion is hardly exotic. And alloca() is a good way to allocate small
amounts of local dynamic memory in a C function and not have to worry about
calling free() before returning.
Well, exotic in the sense that you know you have to keep an extra eye
on stack usage when you use these constructs[0]. At least I do, and
I'm not above average.

/Jorgen

[0] But see the current flame war on tail recursion over in c.l.c++.
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
Rainer Weikusat
2017-06-27 12:56:46 UTC
Permalink
[...]
Post by Jorgen Grahn
Post by William Ahern
alloca()
And yes, that one too.
And yet another (perhaps obvious) one: recursion.
Still, these are all rather exotic, and I'd expect people to be aware
that you can't let untrusted data control how you use them. It's a
standard criticism against VLAs, for example.
This deserves some emphasis: Code like

if (!output_charset_cached)
{
const char *value = getenv ("OUTPUT_CHARSET");
if (value != NULL && value[0] != '\0')
{
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
output_charset_cache = value_copy;
}
output_charset_cached = 1;
}

https://code.woboq.org/userspace/glibc/intl/dcigettext.c.html#get_output_charset

is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!". As alaways, the core of the problem is lack of
input validation (This can't be so difficult, can it?) and as as
always, the people who can apparently never be bothered to implement
that cry for the kernel, the compiler and the universe in general to be
changed in order to accomodate their (lazy) coding habits.

There are certainly grammatical/ syntactical requirements for this
parameter and the program MUST NOT touch it before conformance to these
requirements has been checked.

The sudo vulnerability is also 'brilliant': The program considers
argv[0] 'trusted data' and can thus be made to recurse down a
world-writable directory (/dev/shm).

<sarcasm>
The obvious solution is to use a filesystem which support neither symlinks
nor hardlinks!
</sarcasm>
s***@potato.field
2017-06-27 13:43:25 UTC
Permalink
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!". As alaways, the core of the problem is lack of
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Post by Rainer Weikusat
input validation (This can't be so difficult, can it?) and as as
Input validation is nothing more than parsing and it can also have bugs.
Proper parsing is hard, people tend to forget this.
--
Spud
Rainer Weikusat
2017-06-27 14:40:20 UTC
Permalink
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?

Moreover, if __dcigettext() finds the message to be translated,
then _nl_find_msg() strdup()licates the OUTPUT_CHARSET
environment variable and allows a local attacker to immediately
smash the stack and gain control of the instruction pointer (the
eip register, on i386), as detailed in Step 4a.

[...]

Step 4a: Smash the stack with the heap

The self-contained gettext() exploitation method malloc()ates
and memcpy()es a "smashing-chunk" of up to 128KB (the
OUTPUT_CHARSET environment variable) that smashes memcpy()'s
stack-frame and return-address, as explained in II.3.4.

https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt

That's not just "a string on the heap" but a large amount of data
supplied by an untrusted user conveniently copied onto the program stack
for no particular reason and ...
Post by s***@potato.field
Post by Rainer Weikusat
As alaways, the core of the problem is lack of
input validation (This can't be so difficult, can it?) and as as
Input validation is nothing more than parsing and it can also have bugs.
Proper parsing is hard, people tend to forget this.
... the 'input parser' whose sole purpose is to ensure that the copied
data contains exactly one zero byte at the end of it is indeed
completely fucked-up.

Proper parsing is tedious and people prefer to do a Seggelmann aka "not
relevant for my security paper, I'll do it later" in such situaitons.
s***@potato.field
2017-06-27 14:52:54 UTC
Permalink
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end. There's obviously some subtle bug in that sub system
that I'm not aware of.
--
Spud
Barry Margolin
2017-06-27 14:58:40 UTC
Permalink
Post by s***@potato.field
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end. There's obviously some subtle bug in that sub system
that I'm not aware of.
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
--
Barry Margolin, ***@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
Casper H.S. Dik
2017-06-27 19:05:28 UTC
Permalink
Post by Barry Margolin
Post by s***@potato.field
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end. There's obviously some subtle bug in that sub system
that I'm not aware of.
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Right. Each environment variable is '\0' delimited.

Casper
Rainer Weikusat
2017-06-27 19:26:16 UTC
Permalink
Post by Barry Margolin
Post by s***@potato.field
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end. There's obviously some subtle bug in that sub system
that I'm not aware of.
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.

But considering that the code which copied the exploit payload into the
location activating it was

size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);

there's shouldn't ever have been any doubt on this.
s***@potato.field
2017-06-28 08:32:26 UTC
Permalink
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps I'm just dumb, but I'm still not seeing the fatal bug in that code.
It looks perfectly correct to me.
--
Spud
Rainer Weikusat
2017-06-28 11:17:41 UTC
Permalink
Post by s***@potato.field
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps
you're playing dumb.

I already thought so.
Rainer Weikusat
2017-06-28 11:20:27 UTC
Permalink
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps
you're playing dumb.
I already thought so.
Addition: It's no wonder that software ends up with 'unintended features'
of this kind when people still stubbornly defend this practice after it
has blown up into the face of somebody else, as documented in
painstaking detail in the original paper.
s***@potato.field
2017-06-28 12:51:28 UTC
Permalink
On Wed, 28 Jun 2017 12:17:41 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps
you're playing dumb.
I already thought so.
I'm not playing dumb. Where is the error?? Point it out on the specific line!
The only possible one I can see is if value is NULL which will crash strlen(),
otherwise...
--
Spud
Scott Lurndal
2017-06-28 13:24:51 UTC
Permalink
Post by s***@potato.field
On Wed, 28 Jun 2017 12:17:41 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps
you're playing dumb.
I already thought so.
I'm not playing dumb. Where is the error?? Point it out on the specific line!
The only possible one I can see is if value is NULL which will crash strlen(),
otherwise...
It wouldn't crash on BSD systems - they'd map a read-only page of zeros
at VA 0 - specifically to cause strlen to return zero on such cases.
Casper H.S. Dik
2017-06-28 13:50:41 UTC
Permalink
Post by Scott Lurndal
It wouldn't crash on BSD systems - they'd map a read-only page of zeros
at VA 0 - specifically to cause strlen to return zero on such cases.
So then you'd read 1 byte at 0 and copy that to an just allocated byte.

I would assue, though, that BSD has long stopped mapping a page of
zero at address 0.

Casper
Scott Lurndal
2017-06-28 14:19:50 UTC
Permalink
Post by Casper H.S. Dik
Post by Scott Lurndal
It wouldn't crash on BSD systems - they'd map a read-only page of zeros
at VA 0 - specifically to cause strlen to return zero on such cases.
So then you'd read 1 byte at 0 and copy that to an just allocated byte.
I would assue, though, that BSD has long stopped mapping a page of
zero at address 0.
I'm surprised you don't remember this stuff - when the ucb stuff
was integrated into SVR4 (which IIRC, Sun had a role in), there were
a number of issues related to the the ucbcmd subtree which expected
a RO page of zeros at VA 0. All those commands had to be fixed as part
of the port to SVR4.
Casper H.S. Dik
2017-06-29 07:05:29 UTC
Permalink
Post by Scott Lurndal
Post by Casper H.S. Dik
Post by Scott Lurndal
It wouldn't crash on BSD systems - they'd map a read-only page of zeros
at VA 0 - specifically to cause strlen to return zero on such cases.
So then you'd read 1 byte at 0 and copy that to an just allocated byte.
I would assue, though, that BSD has long stopped mapping a page of
zero at address 0.
I'm surprised you don't remember this stuff - when the ucb stuff
was integrated into SVR4 (which IIRC, Sun had a role in), there were
a number of issues related to the the ucbcmd subtree which expected
a RO page of zeros at VA 0. All those commands had to be fixed as part
of the port to SVR4.
I remeber that; however, even SunOS 4.x did not map a page at address 0;
but for some reason well SunOS 4 & SV merged to get SVr4, the "UCB"
compatibiility changes looked more like BSD 4.1 or perhaps 4.2 but certainly
not like 4.3. I wasn't at Sun at that time, though, but using SunOS since
SunOS 3.2 or some such.

The worst problem when it comes to NULL pointers were NULLs being passed as
arguments to %s printf() format specifiers. In the end, and very late
in the game, Solaris now prints (null). SunOS <= 4* already did that
(just verified the SunOS 3.5 source). Very late here means Solaris 11
and patches in Solaris 10.

Probably because we felt that sending patches upstream for included
FOSS or maintaining them separately became unsustainable.

Earlier we already had a ***@0.so which can be preloaded (introduced in
Solaris 2.6)

Casper
Rainer Weikusat
2017-06-28 14:03:40 UTC
Permalink
Post by s***@potato.field
On Wed, 28 Jun 2017 12:17:41 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 20:26:16 +0100
Post by Rainer Weikusat
Post by Barry Margolin
Right. Doesn't the kernel depend on the trailing null when copying the
environment during exec()?
Linux does this copy in copy_strings in fs/exec.c. The copy-loop ensures
that each environment string is 0-terminated and smaller than 32 *
PAGE_SIZE.
But considering that the code which copied the exploit payload into the
location activating it was
size_t len = strlen (value) + 1;
char *value_copy = (char *) malloc (len);
if (value_copy != NULL)
memcpy (value_copy, value, len);
there's shouldn't ever have been any doubt on this.
Perhaps
you're playing dumb.
I already thought so.
I'm not playing dumb. Where is the error?? Point it out on the specific line!
The only possible one I can see is if value is NULL which will crash strlen(),
otherwise...
That's explained in the original paper. There's no error in this code,
however, after the stack has been made to run into the heap, this copy
of an user-supplied value can be utilized to overwrite the stack frame
of the memcpy with exploit data in order to cause a control transfer to
some more 'interesting' C library function instead.

The only restriction for this data is that it cannot contain 0-bytes as
the first 0-byte would be interpreted as end of the value. As far as I
could determine (in fine open sauce tradition, the purpose of OUTPUT_CHARSET
seems to be undocumented), valid values are character sets, that is,
encodings, known to the local system, eg, utf-8 or something like this
(in fine open sauce tradition, it seems impossible to determine how to
use the gettext command without reading the source as the documentation
references undefined technical terms). If this was checked prior to
copying the value, using this for an exploit would at least be much more
difficult.
s***@potato.field
2017-06-28 14:36:14 UTC
Permalink
On Wed, 28 Jun 2017 15:03:40 +0100
Post by s***@potato.field
Post by s***@potato.field
The only possible one I can see is if value is NULL which will crash
strlen(),
Post by s***@potato.field
otherwise...
That's explained in the original paper. There's no error in this code,
however, after the stack has been made to run into the heap, this copy
of an user-supplied value can be utilized to overwrite the stack frame
of the memcpy with exploit data in order to cause a control transfer to
some more 'interesting' C library function instead.
So in other words you'd have to have a huge stack frame and or a huge heap
before this will work and even then I assume you'd have to have a known size of
data for the correct exploit code to be run.

I've no idea how OS's in general cope with the stack and the heap of a process
colliding but I would have assumed the kernel would spot it and dump core
before anything nasty could happen.
--
Spud
Richard Kettlewell
2017-06-28 14:43:20 UTC
Permalink
Post by s***@potato.field
I've no idea how OS's in general cope with the stack and the heap of a
process colliding but I would have assumed the kernel would spot it
and dump core before anything nasty could happen.
It can’t, without some help from the user process.
--
http://www.greenend.org.uk/rjk/
s***@potato.field
2017-06-28 15:21:29 UTC
Permalink
On Wed, 28 Jun 2017 15:43:20 +0100
Post by s***@potato.field
I've no idea how OS's in general cope with the stack and the heap of a
process colliding but I would have assumed the kernel would spot it
and dump core before anything nasty could happen.
It can’t, without some help from the user process.
Isn't heap allocation a kernel function? In which case surely it could just
check the stack and heap pointers each time memory is allocated to the heap
and make sure they haven't crossed?
--
Spud
Richard Kettlewell
2017-06-28 15:36:37 UTC
Permalink
Post by s***@potato.field
Post by Richard Kettlewell
Post by s***@potato.field
I've no idea how OS's in general cope with the stack and the heap of a
process colliding but I would have assumed the kernel would spot it
and dump core before anything nasty could happen.
It can’t, without some help from the user process.
Isn't heap allocation a kernel function? In which case surely it could
just check the stack and heap pointers each time memory is allocated
to the heap and make sure they haven't crossed?
It can’t detect the stack growing down into some other writable mapping
(including, but not only, the heap) that way.
--
http://www.greenend.org.uk/rjk/
Casper H.S. Dik
2017-06-29 07:11:29 UTC
Permalink
Post by s***@potato.field
Post by s***@potato.field
I've no idea how OS's in general cope with the stack and the heap of a
process colliding but I would have assumed the kernel would spot it
and dump core before anything nasty could happen.
It can’t, without some help from the user process.
Isn't heap allocation a kernel function? In which case surely it could
just check the stack and heap pointers each time memory is allocated
to the heap and make sure they haven't crossed?
It can’t detect the stack growing down into some other writable mapping
(including, but not only, the heap) that way.
Indeed; typcally growing the stack is detected by referencing a page in
stack segment and then grow the stack to that point. But if the stack
pointer is now referencing far beyond the stack segment, we can't know
because a stack pointer doesn't taste different from another pointer.

Casper
Rainer Weikusat
2017-06-28 19:29:31 UTC
Permalink
Post by s***@potato.field
On Wed, 28 Jun 2017 15:03:40 +0100
Post by s***@potato.field
Post by s***@potato.field
The only possible one I can see is if value is NULL which will crash
strlen(),
Post by s***@potato.field
otherwise...
That's explained in the original paper. There's no error in this code,
however, after the stack has been made to run into the heap, this copy
of an user-supplied value can be utilized to overwrite the stack frame
of the memcpy with exploit data in order to cause a control transfer to
some more 'interesting' C library function instead.
So in other words you'd have to have a huge stack frame and or a huge heap
before this will work and even then I assume you'd have to have a known size of
data for the correct exploit code to be run.
I've no idea how OS's in general cope with the stack and the heap of a process
colliding but I would have assumed the kernel would spot it and dump core
before anything nasty could happen.
Below is the runtime memory map of a very simple (64-bit) Linux process (which
just calls pause):

00400000-00401000 r-xp 00000000 08:05 528362 /usr/local/bin/pause
00600000-00601000 rw-p 00000000 08:05 528362 /usr/local/bin/pause
7fd628e34000-7fd628fb8000 r-xp 00000000 08:01 35852 /lib/x86_64-linux-gnu/libc-2.13.so
7fd628fb8000-7fd6291b7000 ---p 00184000 08:01 35852 /lib/x86_64-linux-gnu/libc-2.13.so
7fd6291b7000-7fd6291bb000 r--p 00183000 08:01 35852 /lib/x86_64-linux-gnu/libc-2.13.so
7fd6291bb000-7fd6291bc000 rw-p 00187000 08:01 35852 /lib/x86_64-linux-gnu/libc-2.13.so
7fd6291bc000-7fd6291c1000 rw-p 00000000 00:00 0
7fd6291c4000-7fd6291e4000 r-xp 00000000 08:01 35850 /lib/x86_64-linux-gnu/ld-2.13.so
7fd6293c2000-7fd6293c4000 rw-p 00000000 00:00 0
7fd6293e3000-7fd6293e4000 r--p 0001f000 08:01 35850 /lib/x86_64-linux-gnu/ld-2.13.so
7fd6293e4000-7fd6293e5000 rw-p 00020000 08:01 35850 /lib/x86_64-linux-gnu/ld-2.13.so
7fd6293e5000-7fd6293e8000 rw-p 00000000 00:00 0
7ffef5053000-7ffef5074000 rw-p 00000000 00:00 0 [stack]
7ffef50b4000-7ffef50b7000 r--p 00000000 00:00 0 [vvar]
7ffef50b7000-7ffef50b9000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

There's r/w-area from 7fd6293e5000-7fd6293e8000, followed by a hole of
roughly 163G followed by the stack. The stack grows downward. This is
supposed to work such that a program tries a write access somewhere in
the hole between the stack and the area below it. As there's nothing
mapped there, this will cause a page fault and the kernel page fault
handler is supposed to reply to that by growing the stack accordingly
and restarting the faulting instruction.

But if a stack allocation jumps over all of the remaining hole (which is
much smaller on a 32-bit system), the memory access won't cause a page
fault as it's within an area with memory allocated to it and the kernel
will never know about this.
Barry Margolin
2017-06-28 20:12:37 UTC
Permalink
Post by Rainer Weikusat
Post by s***@potato.field
On Wed, 28 Jun 2017 15:03:40 +0100
Post by s***@potato.field
Post by s***@potato.field
The only possible one I can see is if value is NULL which will crash
strlen(),
Post by s***@potato.field
otherwise...
That's explained in the original paper. There's no error in this code,
however, after the stack has been made to run into the heap, this copy
of an user-supplied value can be utilized to overwrite the stack frame
of the memcpy with exploit data in order to cause a control transfer to
some more 'interesting' C library function instead.
So in other words you'd have to have a huge stack frame and or a huge heap
before this will work and even then I assume you'd have to have a known size of
data for the correct exploit code to be run.
I've no idea how OS's in general cope with the stack and the heap of a process
colliding but I would have assumed the kernel would spot it and dump core
before anything nasty could happen.
Below is the runtime memory map of a very simple (64-bit) Linux process (which
00400000-00401000 r-xp 00000000 08:05 528362
/usr/local/bin/pause
00600000-00601000 rw-p 00000000 08:05 528362
/usr/local/bin/pause
7fd628e34000-7fd628fb8000 r-xp 00000000 08:01 35852
/lib/x86_64-linux-gnu/libc-2.13.so
7fd628fb8000-7fd6291b7000 ---p 00184000 08:01 35852
/lib/x86_64-linux-gnu/libc-2.13.so
7fd6291b7000-7fd6291bb000 r--p 00183000 08:01 35852
/lib/x86_64-linux-gnu/libc-2.13.so
7fd6291bb000-7fd6291bc000 rw-p 00187000 08:01 35852
/lib/x86_64-linux-gnu/libc-2.13.so
7fd6291bc000-7fd6291c1000 rw-p 00000000 00:00 0
7fd6291c4000-7fd6291e4000 r-xp 00000000 08:01 35850
/lib/x86_64-linux-gnu/ld-2.13.so
7fd6293c2000-7fd6293c4000 rw-p 00000000 00:00 0
7fd6293e3000-7fd6293e4000 r--p 0001f000 08:01 35850
/lib/x86_64-linux-gnu/ld-2.13.so
7fd6293e4000-7fd6293e5000 rw-p 00020000 08:01 35850
/lib/x86_64-linux-gnu/ld-2.13.so
7fd6293e5000-7fd6293e8000 rw-p 00000000 00:00 0
7ffef5053000-7ffef5074000 rw-p 00000000 00:00 0
[stack]
7ffef50b4000-7ffef50b7000 r--p 00000000 00:00 0
[vvar]
7ffef50b7000-7ffef50b9000 r-xp 00000000 00:00 0
[vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
There's r/w-area from 7fd6293e5000-7fd6293e8000, followed by a hole of
roughly 163G followed by the stack. The stack grows downward. This is
supposed to work such that a program tries a write access somewhere in
the hole between the stack and the area below it. As there's nothing
mapped there, this will cause a page fault and the kernel page fault
handler is supposed to reply to that by growing the stack accordingly
and restarting the faulting instruction.
But if a stack allocation jumps over all of the remaining hole (which is
much smaller on a 32-bit system), the memory access won't cause a page
fault as it's within an area with memory allocated to it and the kernel
will never know about this.
Problems like this are why Multics was designed to use segmented memory,
rather than a flat memory architecture. There were separate segments for
the heap, stack, linkage, and program text, so they could never collide
with each other.

The x86 CPU architecture has segments, but modern operating systems
don't make use of them (from what I understand, there are limitations on
how they can be used, which makes them inconvenient -- or maybe I'm
thinking of rings).
--
Barry Margolin, ***@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
s***@potato.field
2017-06-29 08:34:50 UTC
Permalink
On Wed, 28 Jun 2017 20:29:31 +0100
Post by Rainer Weikusat
But if a stack allocation jumps over all of the remaining hole (which is
much smaller on a 32-bit system), the memory access won't cause a page
fault as it's within an area with memory allocated to it and the kernel
will never know about this.
Now I get it. Thanks.
--
Spud
Casper H.S. Dik
2017-06-29 07:09:22 UTC
Permalink
Post by s***@potato.field
I've no idea how OS's in general cope with the stack and the heap of a process
colliding but I would have assumed the kernel would spot it and dump core
before anything nasty could happen.
That is where things explode and the situation becomes exploitable.

The Solaris heap is brk(2) based and so it does realize that there is no
room past the heap, e.g., when we the stack is too close: it will find the
stack's guard page; the stack has no standard mechanism to check for
stack overflows and while it typically has a guard page, that is generally
not big enough.

Casper
Rainer Weikusat
2017-06-27 15:19:47 UTC
Permalink
Post by s***@potato.field
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and fucks
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end.
,----
| [...]
|
| ... the 'input parser' whose sole purpose is to ensure that the copied
| data contains exactly one zero byte at the end of it is indeed
| completely fucked-up.
`----

I don't understand how you came up with this supposition, considering
that my posting explictly stated the opposite, that the code excerpt I
posted used strlen and that the exploit description I quoted didn't talk
about this.
s***@potato.field
2017-06-28 08:31:28 UTC
Permalink
On Tue, 27 Jun 2017 16:19:47 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 15:40:20 +0100
Post by Rainer Weikusat
Post by s***@potato.field
On Tue, 27 Jun 2017 13:56:46 +0100
Post by Rainer Weikusat
is just asking for trouble: In a setuid-program, OUTPUT_CHARSET is
provided by an untrusted user and blindly copying it onto the heap like
this cries "Exploit me!".
No it doesn't. Until some buggy piece of code attempts to parse it and
fucks
Post by s***@potato.field
Post by Rainer Weikusat
Post by s***@potato.field
it up its nothing more than a string of bytes on the heap.
Considering that you have (or could have) the exploit in front of your
nose, that's a pretty bizarre statement, is it not?
I don't understand how you could set an enviroment variable value that didn't
have a \0 on the end.
,----
| [...]
|
| ... the 'input parser' whose sole purpose is to ensure that the copied
| data contains exactly one zero byte at the end of it is indeed
| completely fucked-up.
`----
I don't understand how you came up with this supposition, considering
that my posting explictly stated the opposite, that the code excerpt I
posted used strlen and that the exploit description I quoted didn't talk
about this.
Sorry, perhaps I misunderstood. I've rather lost interest anyway tbh.
--
Spud
Rainer Weikusat
2017-06-28 11:23:20 UTC
Permalink
[...]
Post by William Ahern
Post by Rainer Weikusat
The solution to this is to fix the code, not come up with yet another
bizarre exploit mitigation scheme ...
The correct solution is compiler stack probes, pure and simple.
The 'correct' solution is "don't process untrusted data unverified", no
matter how innocent this may seem. At least from a coding standpoint.

To which degree this is "socially realistic" would be another question
...
Kaz Kylheku
2017-06-22 22:13:49 UTC
Permalink
Post by Noob
Hello,
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
Ran into this a decade ago.

Picture a gigantic application written in C++, with lots and lots of
threads. VM footprint of like 6 Gb, due to the memory mapping of all
those thread stacks. (Glibc pthreads stacks: 2Mb a pop, by default).

I reduced the VM footprint considerably by giving threads 64 Kb stacks.

(Except or the main thread: glibc's ld.so uses alloca when resolving
symbols. We had lots and lots and lots of symbols, due to lots and
lots of generated C++ classes. By binary search, I discovered we needed
well over a megabyte of stack before main() got even called,
ust to resolve dynamic symbols.)

However, we soon ran into a bug whereby a thread jumped over a guard
page and went over another thread's stack. The issue appeared right
when we committed the change to shrink the stacks.

The issue occured in some third party code.

To heck with it, let's drop some names: IP routing stack code from DCL.

This code had some debugging preprocessor macros. Hidden behind those
macros were stack allocations like this:

char msg_buf[8192];

for holding the formatted debug trace messages. Totally inappropriate
for embedded use (but at the time I couldn't articulate any criticism of
this such that our own 6Gb-footprint-with-shitloads-of-threads
monstrosity would escape that same criticism unscathed.)

When I was called in to debug into this, I found this pretty quickly.
Needless to say, one these allocations was cheerfully straddling over a
4096 byte guard page. The guard was never hit because debugging wasn't
even turned on, and so the buffer was never filled with anything. And
even if it had been, the debug message could easily have been too short
to touch the page. E.g suppose printf_buf starts 80 bytes below the
guard page which was just skipped (thus, in the next thread's stack) and
the message is only 79 bytes plus terminating null. No fault!
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That would be a darn good idea.

As for DCL, I'm Googling for "DCL" and "IP routing" now. No hits newer
than around 2005-2006. Didn't survive the economic downturn?

Big surprise there ...
Casper H.S. Dik
2017-06-23 06:03:46 UTC
Permalink
Post by Kaz Kylheku
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That would be a darn good idea.
The Solaris Stdio compilers have an option which will do
exactly that (-xcheck=stkovf:check)

Casper
Rainer Weikusat
2017-06-23 16:28:12 UTC
Permalink
Post by Casper H.S. Dik
Post by Kaz Kylheku
Post by Noob
And perhaps VS by default inserts code to write to every
stack page when allocating more stack? (Which is sure to
trigger a fault when trying to jump over the guard pages)
That would be a darn good idea.
The Solaris Stdio compilers have an option which will do
exactly that (-xcheck=stkovf:check)
According to the original text, gcc, at least 'newer versions' support
this, too.
Thomas Jahns
2017-06-23 09:49:27 UTC
Permalink
Post by Noob
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
On a related note: I'd recommend to always limit the stack of programs since an
unlimited stack also hides lots of other spatial memory errors (because the
kernel will happily assume it's just another need for more stack memory).

Thomas
William Ahern
2017-06-23 19:32:26 UTC
Permalink
Post by Thomas Jahns
Post by Noob
I suppose most of you have been aware of the "stack clash"
variant on stack smashing.
On a related note: I'd recommend to always limit the stack of programs since an
unlimited stack also hides lots of other spatial memory errors (because the
kernel will happily assume it's just another need for more stack memory).
Limiting the size of the stack isn't a long-term solution. Theoretically it
could make an exploit even easier in some contexts. Increasing the size of
VM stack guards is better, but still merely probabilistic.

The fundamental issue is that the compiler isn't emitting code that
correctly enforces (directly or indirectly) the stack limit. The only
correct solution is for the compiler to emit the proper code that ensures a
function invocation or automatic object allocation doesn't blow the stack,
or if it blows the stack does so in a reliably and consistently detectable
manner. The natural way to do this on Unix systems is to emit so-called
stack probes that will always touch a stack guard page _before_ any memory
after the guard page might be dereferenced. To do that you have to move the
stack frame pointer in page-sized increments with intervening loads within
the next page-sized window.

Stack guards were added by kernels and C runtimes as exploit and bug
mitigations. They were probabilistic. Over the years people began to _rely_
on their operation. But they're not 100% reliable without compiler
assistance. However, the compilers never participated. Stack probes are
broken in LLVM and GCC, and in any event not enabled by default.
Continue reading on narkive:
Loading...