Why was Intel a no-show on No Execute?

  • Thread starter Thread starter Yousuf Khan
  • Start date Start date
Robert said:
I don't think so. For a recursive function there are many
calls, possibly flooding out the hw return stack. But every
call has a return, and that address _is_ correct on both the
hw and memory stacks.

You don't call any other function in your recursive functions? :-)
Excellent! I do not suggest trapping out overflows.
They're to occur on deep recursion which should not contain
evil getch() calls. Just trap misses.

As far as I can tell, and with the exception of recursive
functions which call no other function, RAS overflow will
cause a RET misprediction.
 
Benny Amorsen said:
I think the ELKS people will be saddened to hear that.

So, it never surprises me to find Linux being ported to do something or
another at some point in time. I guess the question these days to ask is
whether there is something Linux hasn't been ported to? Commodore 64? Apple
II?

Yousuf Khan
 
Robert Redelmeier said:
True in a literal sense.

But `c` compilers have this habit of allocating local variable
space on the stack. So when `char input[80];` is coded in a
routine, ESP gets decreased by 80 and that array is sitting
just below the return address!

I don't think it's _required_ by any standard that local vars are
allocated on the stack, but it sure makes memory managment easy.

AFAIK, only global vars and large malloc()s are put on the heap.

Still the only place where code can be executed from is the CS segment. You
cannot write anything into the CS segment once you're running, so even if
they screw up the stack somehow, then worst that's going to happen is that
the program will stop working and have to be shut down by the OS. Which is
basically what NX on the page tables does these days.

Segments would've fully protected everything.

Yousuf Khan
 
In comp.arch Yousuf Khan said:
Nope, won't work. Segmentation would protect it completely. There is no way
for data written to the heap to touch the data in the stack. Stack segment

But procedure local variables (including arrays) don't live in the heap,
they live on the stack.
and data segment are separate. It's like as if the stack had its own
container, the code has its own, and the data heap its own. What happens in
one container won't even reach the other containers.

Doesn't matter. All you need for an exploit is to be able to make *one*
system call. And for that, you don't need to write to the code segment
at all. The stack is enough.
 
Sander Vesik said:
Doesn't matter. All you need for an exploit is to be able to make
*one* system call. And for that, you don't need to write to the code
segment at all. The stack is enough.

The only place you can run code is from the code segment. If you insert code
into the stack segment, none of it will be executable. At best it might end
up causing the return address to go to the wrong part of the code segment
and therefore run the program from the wrong point, but more likely the
program will just end up locking up and be shutdown by the OS.

Yousuf Khan
 
The only place you can run code is from the code segment. If you insert code
into the stack segment, none of it will be executable. At best it might end
up causing the return address to go to the wrong part of the code segment
and therefore run the program from the wrong point, but more likely the
program will just end up locking up and be shutdown by the OS.

Changing branch address and stack values that get loaded to
arument registers (or just plain stack values on a stack machine)
are enough.

An object dump of a binary with stack overflow reveals the address
of a "system call" instruction, which is enough to know what return
adress is needed.

i.e. you don't need new code to execute you just need to get to
existing insn's in the binary with the appropriate state, and that
appropriate state can be set up with stack only overwriting.

Period.

Peter
 
Yousuf Khan said:
The only place you can run code is from the code segment. If you insert code
into the stack segment, none of it will be executable. At best it might end
up causing the return address to go to the wrong part of the code segment
and therefore run the program from the wrong point, but more likely the
program will just end up locking up and be shutdown by the OS.

Yousuf Khan

Yousuf,

Check out the following link:

http://packetstormsecurity.nl/groups/horizon/stack.txt

which explains how you can do overflow attack
when stack is not executable.
Although this is illustrated in Solaris/SPARC,
it equally applies to any x86.

Seongbae
 
Segments would've fully protected everything.

Your assurance is endearing. But re-read the thread for a counter example
where the only code executed (in this process anyway) already exists (it
just forks off a /bin/sh shell).

Segments protect just as "fully" as separate address spaces do.
It's better than nothing, but unless you're extremely careful, it's not
sufficient for real security. Better make sure buffer overflows *can't*
happen, so you can actually reason about properties of your code.


Stefan
 
In comp.sys.ibm.pc.hardware.chips Grumble said:
You don't call any other function in your recursive functions? :-)

Hey, I avoid recursion. But if you called another fn,
it too would return.
As far as I can tell, and with the exception of recursive
functions which call no other function, RAS overflow will
cause a RET misprediction.

It should case a RET misprediction even then unless it duplicates
TOS when it pops. For use as a security mechanism, it'd be
better if TOS was tagged empty or missing. Then no MCE.

-- Robert
 
In comp.sys.ibm.pc.hardware.chips Yousuf Khan said:
Still the only place where code can be executed from is the CS segment. You
cannot write anything into the CS segment once you're running, so even if
they screw up the stack somehow, then worst that's going to happen is that
the program will stop working and have to be shut down by the OS. Which is
basically what NX on the page tables does these days.

I can see I haven't made myself clear:
An exploit doesn't need to execute it's own code!

Merely jumping to a suitable place in existing, blessed code
(an exec() call) with nefarious data [stack] is sufficient to
be exploitable.

-- Robert
 
Exception handling is easy -- mismatch produces a MC interrupt.
The kernelspace ISR checks the MSRs which tell it that a return
addr mismatch occurred. Kenel decides what to do -- abort proc,
log, or proceed.

And how does the kernel "decide what to do"?
It's so simple to prevent buffer overflows, there's really no reason to go
to the trouble of some special hardware mechanism to catch some "odd"
behavior which may sometimes catch some forms of buffer-overflow-exploits.
Sure it'll be slow, but how often are calls not paired with returns?

Can be pretty frequent with some languages/compilers, although admittedly
the cost of the misprediction you get with current CPUs is a strong
incentive to try and avoid such situations.


Stefan
 
You don't call any other function in your recursive functions? :-)
Hey, I avoid recursion.

Too bad. Usually makes for clean and simple code, whose security is
simpler to verify.


Stefan
 
In comp.sys.ibm.pc.hardware.chips Stefan Monnier said:
And how does the kernel "decide what to do"?

Whatever it's been programmed to do, likely on a per-process basis.
Likely it'd start APatchy with something like
# /usr/sbin/nooverflow httpd &
It's so simple to prevent buffer overflows, there's really no reason to go

Simple? Writing good code is simple? Wading through millions
of lines of cruft is simple?
to the trouble of some special hardware mechanism to catch some "odd"
behavior which may sometimes catch some forms of buffer-overflow-exploits.

I don't think there are that many forms of buffer overflows.
All result from an open ended IO call like gets().
Do you know any other kinds?
Can be pretty frequent with some languages/compilers, although admittedly

Which ones? mispairing call/ret is a fast way to overflow the stack.

-- Robert
 
And how does the kernel "decide what to do"?
Whatever it's been programmed to do, likely on a per-process basis.
Likely it'd start APatchy with something like
# /usr/sbin/nooverflow httpd &

Say a mismatch (it's not just overflows) happens in a program that uses
exceptions (and where mismatches are hence not necessarily a sign of
a buffer-overflow-exploit): how is the kernel to determine if a given
mismatch is harmless?

Trivial: use a language where it's automatically enforced.
I.e. basically any language other than C. Or use a C compiler that goes
through the extra trouble of trying to prevent overflow exploits
(i.e. by allocating stack variables on a separate stack, or by using fat
pointers, or ...).
I don't think there are that many forms of buffer overflows.

Maybe not, but they can happen in many different kinds of code and there can
be many forms of exploits. So it can be between very difficult and
impossible for a low-level system to determine if a given behavior is part
of the normal execution or is the sign of an exploit.


Stefan
 
In comp.sys.ibm.pc.hardware.chips Stefan Monnier said:
Say a mismatch (it's not just overflows) happens in a program that uses
exceptions (and where mismatches are hence not necessarily a sign of
a buffer-overflow-exploit): how is the kernel to determine if a given
mismatch is harmless?

Well if the pgm has designed-in mismatches, the kernel can't
determine it, and the the pgm would have to be run with that
protection disabled. But how many languages (other than asm)
even _allow_ mismatched call/ret?
Maybe not, but they can happen in many different kinds of code and there can
be many forms of exploits. So it can be between very difficult and
impossible for a low-level system to determine if a given behavior is part
of the normal execution or is the sign of an exploit.

Well, actually there is another way. The OS could monitor
events like return adress mismatches and take defensive
actions when an increase is noted.

-- Robert
 
In comp.arch Yousuf Khan said:
The only place you can run code is from the code segment. If you insert code

only superficialy true. as you have control of the stack, you can cause any
number of function calls to happen with the parameters of your choice. This
is essentialy the same as running code.
into the stack segment, none of it will be executable. At best it might end
up causing the return address to go to the wrong part of the code segment
and therefore run the program from the wrong point, but more likely the
program will just end up locking up and be shutdown by the OS.

Only if you don't know the addresses of functions and system calls.
 
In comp.arch Robert Redelmeier said:
Whatever it's been programmed to do, likely on a per-process basis.
Likely it'd start APatchy with something like
# /usr/sbin/nooverflow httpd &


Simple? Writing good code is simple? Wading through millions
of lines of cruft is simple?


I don't think there are that many forms of buffer overflows.
All result from an open ended IO call like gets().
Do you know any other kinds?

Yes. Accepting user provided content length and not checking it against
your buffer size.
 
In comp.arch Robert Redelmeier said:
Well if the pgm has designed-in mismatches, the kernel can't
determine it, and the the pgm would have to be run with that
protection disabled. But how many languages (other than asm)
even _allow_ mismatched call/ret?

Consider a user mode threads package that uses get/setcontext()
or setjmp / longjmp and so on.
 
In comp.sys.ibm.pc.hardware.chips Sander Vesik said:
Consider a user mode threads package that uses
get/setcontext() or setjmp / longjmp and so on.

Well, I'm not entirely sure how these constructs are
implemented by the compilers, but I would expect a
simple `jmp` instruction. This does NOT disturb the
hw call/ret stack, nor pose any buffer-overflow danger.

-- Robert
 
Say a mismatch (it's not just overflows) happens in a program that uses
Well if the pgm has designed-in mismatches, the kernel can't
determine it, and the the pgm would have to be run with that
protection disabled. But how many languages (other than asm)
even _allow_ mismatched call/ret?

Any language with exceptions: C++, Java, C (with setjmp/longjmp), ...
Well, actually there is another way. The OS could monitor
events like return adress mismatches and take defensive
actions when an increase is noted.

A buffer-overflow exploit might only need one mismatch.


Stefan
 
Back
Top