Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #45707 > unrolled thread
| Started by | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| First post | 2016-02-26 13:38 +0000 |
| Last post | 2016-03-12 12:44 +0000 |
| Articles | 20 on this page of 38 — 11 participants |
Back to article view | Back to comp.lang.forth
Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-26 13:38 +0000
Re: Avoiding memory access vulnerabilities Albert van der Horst <albert@spenarnc.xs4all.nl> - 2016-02-26 16:05 +0000
Re: Avoiding memory access vulnerabilities Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> - 2016-02-26 17:18 -0500
Re: Avoiding memory access vulnerabilities Raimond Dragomir <raimond.dragomir@gmail.com> - 2016-02-26 22:53 -0800
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-27 07:15 +0000
Re: Avoiding memory access vulnerabilities Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-02-27 04:54 -0600
Re: Avoiding memory access vulnerabilities Bernd Paysan <bernd.paysan@gmx.de> - 2016-02-27 14:27 +0000
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-27 15:31 +0000
Re: Avoiding memory access vulnerabilities Bernd Paysan <bernd.paysan@gmx.de> - 2016-03-01 16:30 +0000
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-02 10:04 +0000
Re: Avoiding memory access vulnerabilities Doug Hoffman <glidedog@gmail.com> - 2016-03-02 08:41 -0500
Re: Avoiding memory access vulnerabilities Bernd Paysan <bernd.paysan@gmx.de> - 2016-03-02 23:22 +0000
Re: Avoiding memory access vulnerabilities humptydumpty <ouatubi@gmail.com> - 2016-03-02 22:57 -0800
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-03 13:50 +0000
Re: Avoiding memory access vulnerabilities Bernd Paysan <bernd.paysan@gmx.de> - 2016-03-04 21:01 +0000
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-06 10:55 +0000
Re: Avoiding memory access vulnerabilities Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> - 2016-02-27 11:31 -0500
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-27 17:14 +0000
Re: Avoiding memory access vulnerabilities Ron Aaron <rambamist@gmail.com> - 2016-02-27 18:59 +0200
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-27 17:04 +0000
Re: Avoiding memory access vulnerabilities Ron Aaron <rambamist@gmail.com> - 2016-02-27 19:18 +0200
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-02-28 14:21 +0000
Re: Avoiding memory access vulnerabilities "Elizabeth D. Rather" <erather@forth.com> - 2016-03-02 21:18 -1000
Re: Avoiding memory access vulnerabilities Albert van der Horst <albert@spenarnc.xs4all.nl> - 2016-03-03 10:23 +0000
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-03 14:22 +0000
Re: Avoiding memory access vulnerabilities Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-03-03 04:53 -0600
Re: Avoiding memory access vulnerabilities Albert van der Horst <albert@spenarnc.xs4all.nl> - 2016-03-03 13:15 +0000
Re: Avoiding memory access vulnerabilities Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-03-04 04:34 -0600
Re: Avoiding memory access vulnerabilities Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-03-04 04:56 -0600
Re: Avoiding memory access vulnerabilities Albert van der Horst <albert@spenarnc.xs4all.nl> - 2016-03-04 12:07 +0000
Re: Avoiding memory access vulnerabilities Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-03-04 09:48 -0600
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-04 17:11 +0000
Re: Avoiding memory access vulnerabilities Albert van der Horst <albert@spenarnc.xs4all.nl> - 2016-03-04 11:55 +0000
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-03 11:33 +0000
Re: Avoiding memory access vulnerabilities Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> - 2016-03-04 20:55 -0500
Re: Avoiding memory access vulnerabilities Mark Wills <markwills1970@gmail.com> - 2016-03-05 02:55 -0800
Re: Avoiding memory access vulnerabilities "Elizabeth D. Rather" <erather@forth.com> - 2016-03-10 22:39 -0800
Re: Avoiding memory access vulnerabilities anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-03-12 12:44 +0000
Page 1 of 2 [1] 2 Next page →
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-02-26 13:38 +0000 |
| Subject | Avoiding memory access vulnerabilities |
| Message-ID | <2016Feb26.143842@mips.complang.tuwien.ac.at> |
A frequent source of vulnerabilities in C is buffer overflows: A
memory access can happen outside the bounds of an array or otherwise
outside the bounds of an object (C terminology, in Forth standard
terminology the relevant term is "contiguous region"); the classic
case is to have a store that overwrites the return address adjacent to
the buffer and leads to arbitrary code execution (in Forth return
addresses are typically not close to buffers, but just consider a
variable or field containing an xt close to the buffer, so Forth is
not immune to that). Or the buffer overflow could be a memory fetch
of data that is then sent to the attacker (the heartbleed
vulnerability was of this type, helped along by overzealous
"optimization").
The typical idea of C and Forth programmers with respect to this kind
of danger is to take more care when programming, so that the
vulnerability is avoided. But that has not worked for C programs, and
I guess it does not work for Forth programs, either, at least for big
ones. Programmers also want to avoid bugs by taking care, but somehow
bugs tend to creep into programs. In the Forth community we tend to
find bugs by testing, but vulnerabilities often don't show up in
regular testing, and I don't think there is a testing regime that is
guaranteed to find all memory-access vulnerabilities.
So what else can we do? The idea I am going to explore here is to
limit the part where you have to take special care to a small part of
the program; that goes nicely with the idea that you write an
application-specific wordset and use that to program the application.
The idea is to make the memory access words that take arbitrary
addresses (@ ! c@ c! etc.) only visible in the small special-care
parts, and use more restricted replacements for these words in the
rest of the program. E.g., one could make them visible with INSECURE
and make them invisible with SECURE. The programmers are responsible
themselves for not making these words available through aliases,
deferred words, etc., and of course for not putting vulnerabilities in
the INSECURE code.
How would the replacements look?
Arrays: here one would have words that take an array descriptor and
index, and perform an index check before performing the fetch or
store. Maybe TH@ TH!, or, based on the FSL, }@ }! (just examples, one
would need a full complement for various types and number of array
dimensions; hmm, this appears to become a large number of words; what
can one do about that?). For iterating over arrays, one would have
macros that iterate from one index to another, delivering one element
per iteration, to make it possible to do the bounds check once instead
of once per iteration. Maybe some more common cases of iterating over
arrays can be optimized with special words or macros.
Simple variables: Actually here the checking is not so important in
principle, because one does not buffer-overflow on V @ or V !.
However, because @ and ! are not visible, and we don't want people to
misuse their replacements, the replacements should be able to do
run-time checks in principle, with the option to disable the checks
after testing; (in contrast to arrays, one can test variable accesses
exhaustively). My idea is to have words @v !v c@v c!v etc., that can
access only variables (checked at run-time by comparing a signature
field beside the data field). The signature is filled with a random
value. Alternatively, one could use a dot-parser recognizer that
accepts V.@ V.! if V is a variable.
Structure fields: Like for variables, the checking is not as essential
as for arrays here, but we have to replace @ ! etc. Let's assume a
structure S with a field F. Typical source code patterns are to have
F @, F !, @ F, or ! (where the stored value points to an S). One
could have a dot-parser that recognizes F.@ F.! @.F and S.! for these
patterns. One could have an option to add a signature field to a
structure, and let the generated code check the signature of the
involved structure. This could actually be quite useful in debugging,
not just for security.
MOVE FILL etc.: One might consider these words secure, because you
pass it the buffer size anyway. However, at least for MOVE the passed
size is derived from the source buffer size and may exceed the target
buffer size; or one uses some address arithmetic for determining the
address and size for FILL, which may also incur errors. So one could
have replacements for these words that take a buffer descriptor and
offset/address for the target, and checks that all accesses performed
lie within the target. I am not sure about this, but the C11 standard
has added variants of many library functions with the target buffer
size as additional parameter (e.g., memmove_s() as
memmove()-replacement).
What is there to do? Refine this sketch some more, do a prototype
implementation, and try how well this idea works when one is
programming, and how much the overhead of checking is (for the
required array checks, and for the optional variable and field
checks).
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [next] | [standalone]
| From | Albert van der Horst <albert@spenarnc.xs4all.nl> |
|---|---|
| Date | 2016-02-26 16:05 +0000 |
| Message-ID | <56d077e7$0$24100$e4fe514c@news.xs4all.nl> |
| In reply to | #45707 |
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes: >A frequent source of vulnerabilities in C is buffer overflows: A >memory access can happen outside the bounds of an array or otherwise >outside the bounds of an object (C terminology, in Forth standard >terminology the relevant term is "contiguous region"); the classic >case is to have a store that overwrites the return address adjacent to >the buffer and leads to arbitrary code execution (in Forth return >addresses are typically not close to buffers, but just consider a >variable or field containing an xt close to the buffer, so Forth is >not immune to that). Or the buffer overflow could be a memory fetch >of data that is then sent to the attacker (the heartbleed >vulnerability was of this type, helped along by overzealous >"optimization"). >The typical idea of C and Forth programmers with respect to this kind >of danger is to take more care when programming, so that the >vulnerability is avoided. But that has not worked for C programs, and >I guess it does not work for Forth programs, either, at least for big >ones. Programmers also want to avoid bugs by taking care, but somehow >bugs tend to creep into programs. In the Forth community we tend to >find bugs by testing, but vulnerabilities often don't show up in >regular testing, and I don't think there is a testing regime that is >guaranteed to find all memory-access vulnerabilities. >So what else can we do? The idea I am going to explore here is to >limit the part where you have to take special care to a small part of >the program; that goes nicely with the idea that you write an >application-specific wordset and use that to program the application. Right. As Forth is programming by extending the language, we make a sublanguage to be actually used and the layer beneath can be hidden. So if there is a risk of malicious or negligent or just ignorant users we must hide the layer below. I just think that that should be done in a per application basis. An example is a prime printing program. It is turnkey, and the user normally just specifies a number range. The only other things a user can have executed are options. So a -l options check whether the user gives a reasonable line length to the program etc. So there is an option wordlist with -a -b -c .... -z . Now the programmer need only be careful not to mess those up. Oops. The idea was that it would work without the programmer being careful... >- anton Groetjes Albert -- Albert van der Horst, UTRECHT,THE NETHERLANDS Economic growth -- being exponential -- ultimately falters. albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
[toc] | [prev] | [next] | [standalone]
| From | Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> |
|---|---|
| Date | 2016-02-26 17:18 -0500 |
| Message-ID | <20160226171816.2a1470dc@_> |
| In reply to | #45707 |
On Fri, 26 Feb 2016 13:38:42 GMT anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote: > A frequent source of vulnerabilities in C is buffer overflows: A > memory access can happen outside the bounds of an array or otherwise > outside the bounds of an object (C terminology, in Forth standard > terminology the relevant term is "contiguous region"); the classic > case is to have a store that overwrites the return address adjacent to > the buffer and leads to arbitrary code execution (in Forth return > addresses are typically not close to buffers, but just consider a > variable or field containing an xt close to the buffer, so Forth is > not immune to that). Or the buffer overflow could be a memory fetch > of data that is then sent to the attacker (the heartbleed > vulnerability was of this type, helped along by overzealous > "optimization"). > Buffer overflows work in C because C uses a combined data stack and return stack, i.e., stack frame. You'd need to ban manipulation of the return stack in Forth. Forth words in the dictionary, while they may contain an XT, are not buffers that can be written to and overflowed via input and print string functions. Preventing the Forth dictionary from being written to would solve the adjacent XT overwrite problem. Rod Pemberton
[toc] | [prev] | [next] | [standalone]
| From | Raimond Dragomir <raimond.dragomir@gmail.com> |
|---|---|
| Date | 2016-02-26 22:53 -0800 |
| Message-ID | <384c9f3f-6352-40d3-b92c-cd6c6c478cd6@googlegroups.com> |
| In reply to | #45709 |
sâmbătă, 27 februarie 2016, 00:17:50 UTC+2, Rod Pemberton a scris: > On Fri, 26 Feb 2016 13:38:42 GMT > anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote: > > > A frequent source of vulnerabilities in C is buffer overflows: A > > memory access can happen outside the bounds of an array or otherwise > > outside the bounds of an object (C terminology, in Forth standard > > terminology the relevant term is "contiguous region"); the classic > > case is to have a store that overwrites the return address adjacent to > > the buffer and leads to arbitrary code execution (in Forth return > > addresses are typically not close to buffers, but just consider a > > variable or field containing an xt close to the buffer, so Forth is > > not immune to that). Or the buffer overflow could be a memory fetch > > of data that is then sent to the attacker (the heartbleed > > vulnerability was of this type, helped along by overzealous > > "optimization"). > > > > Buffer overflows work in C because C uses a combined data stack and > return stack, i.e., stack frame. You'd need to ban manipulation of > the return stack in Forth. Forth words in the dictionary, while > they may contain an XT, are not buffers that can be written to > and overflowed via input and print string functions. Preventing the > Forth dictionary from being written to would solve the adjacent XT > overwrite problem. > > > Rod Pemberton Buffer overflow is not related only to executable memory. May happen on data memory as well with fatal consequences. Writing past a buffer may write to some other critical variables. A system then can become unstable, or "just" crash.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-02-27 07:15 +0000 |
| Message-ID | <2016Feb27.081505@mips.complang.tuwien.ac.at> |
| In reply to | #45709 |
Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> writes:
>Buffer overflows work in C because C uses a combined data stack and
>return stack, i.e., stack frame.
No. Separating the return addresses is not sufficient to preventing
arbitrary code execution. If return addresses were separate,
attackers would overwrite function pointers.
> You'd need to ban manipulation of
>the return stack in Forth.
Standard-conforming use of >R, R@, R> are not a danger, because these
values are not used as return values, and there are no buffers on the
return stack. Bugs involving these words that accidentially treat
data as return addresses are also unlikely in tested programs, because
such bugs cause crashes pretty much every time the code is executed.
I'll leave the analysis of the safety of N>R NR> to fans of these
words.
Concerning (non-standard) return-address manipulations, many people
just drop return addresses; a danger is that what then is returned to
is data, but, as with standard usage of >R R>, this would not survive
testing and debugging. Another common pattern is that various return
addresses are duped or swapped around; there is no way for data to get
in there, except via a bug, and again, such a bug would be very
obvious in testing and would not survive until production.
One danger would to store such a return address in data space, and
then use it as return address later; this would allow a buffer
overflow vulnerability to overwrite this value and lead to arbitrary
code execution. But I have never seen that kind of usage, and in any
case, it is not any more of a danger than xts in the dictionary.
One related danger, however, is the address of the last exception
frame; Gforth keeps that in the dictionary, and it is probably not the
only Forth system; if the attacker can change that and cause a taken
THROW (e.g., by sending a signal to the process), he can execute
arbitrary code. Overwriting some deferred word is probably an easier
attack, though.
>Forth words in the dictionary, while
>they may contain an XT, are not buffers that can be written to
>and overflowed via input and print string functions.
Counterexample:
create buf 100 allot
create two-xts ' . , ' . ,
buf 1000 untrusted-file read-file
>Preventing the
>Forth dictionary from being written to would solve the adjacent XT
>overwrite problem.
It would not solve these problems (see below), but it would prevent
most standard programs from working, because they tend to have at
least some data in data space that they write to.
You can also have a buffer overflow in the heap with an adjacent xt.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2016-02-27 04:54 -0600 |
| Message-ID | <usydnTf95PjUHUzLnZ2dnUU78bWdnZ2d@supernews.com> |
| In reply to | #45717 |
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote: > Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> writes: > >>Preventing the Forth dictionary from being written to would solve >>the adjacent XT overwrite problem. > > It would not solve these problems (see below), but it would prevent > most standard programs from working, because they tend to have at > least some data in data space that they write to. > > You can also have a buffer overflow in the heap with an adjacent xt. One other thing: AIUI, some Forths have local buffers on the return stack. That's perhaps not the best innovation. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-02-27 14:27 +0000 |
| Message-ID | <nasbol$614$1@dont-email.me> |
| In reply to | #45717 |
Am Sat, 27 Feb 2016 07:15:05 +0000 schrieb Anton Ertl: >>Preventing the Forth dictionary from being written to would solve the >>adjacent XT overwrite problem. > > It would not solve these problems (see below), but it would prevent most > standard programs from working, because they tend to have at least some > data in data space that they write to. Having a dictionary layout similar to a Flash system (write-once code in one segment, write-many buffers and variables in another segment) could prevent accidential overwriting of code. This sort of layout also helps native code Forths (no conflict between data and code in the cache). Since we can't put buffers on the return or data stack, we put buffers into the dictionary, creating just a different sort of buffer overflow problem. The thing that helps Forth a lot more against buffer overflow is that the length of a buffer operation is always explicit. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-02-27 15:31 +0000 |
| Message-ID | <2016Feb27.163105@mips.complang.tuwien.ac.at> |
| In reply to | #45719 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Sat, 27 Feb 2016 07:15:05 +0000 schrieb Anton Ertl:
>Having a dictionary layout similar to a Flash system (write-once code in
>one segment, write-many buffers and variables in another segment) could
>prevent accidential overwriting of code.
I am not sure that accidental overwriting of data is better (even if
it is not code addresses).
As for vulnerabilities: In Gforth the native code is separated from
the data, but the headers, including the code address, are mixed with
the data, and that is enough to allow arbitrary code execution. If
you have
create buf 100 allot
variable foo
and the attacker can write beyond the end of BUF, you don't even need
to store an xt in FOO and later EXECUTE it to get arbitrary code
execution; overwriting the code address of FOO is enough if FOO is
later text-interpreted, EXECUTEd, or somesuch.
Separate headers help against that, but there is still the issue of
xts in the data. So the way to deal with that is to get rid of the
buffer overflows; trying to prevent arbitrary code execution while
leaving the buffer overflows does not work in the general case (and
rarely in specific cases).
>The thing that helps Forth a lot more against buffer overflow
>is that the length of a buffer operation is always explicit.
It probably reduces the number of such vulnerabilities, but the
programmer can still make a mistake when passing the buffer length.
Storing the buffer length (not content length) before the start of the
buffer, and letting all words that write to a buffer check that length
should reduce the number of such vulnerabilities further.
Disadvantage: We would need variants of buffer-writing words that do
that check, just like the C11 people have all these ..._s library
functions. The word counters will not like it.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-03-01 16:30 +0000 |
| Message-ID | <nb4g3v$egg$1@dont-email.me> |
| In reply to | #45720 |
Am Sat, 27 Feb 2016 15:31:05 +0000 schrieb Anton Ertl: > Bernd Paysan <bernd.paysan@gmx.de> writes: >>Am Sat, 27 Feb 2016 07:15:05 +0000 schrieb Anton Ertl: >>Having a dictionary layout similar to a Flash system (write-once code in >>one segment, write-many buffers and variables in another segment) could >>prevent accidential overwriting of code. > > I am not sure that accidental overwriting of data is better (even if it > is not code addresses). Depends on the data. You want to overwrite deferred words or similar to get remote execution. The usual layout of a flash-based Forth has only the buffer or the variable space in read-write RAM, and all the rest (including headers and code fields) is in flash. This is "good enough" even for native code Forths, as on current processors, data can be in the data and the code cache as long as it is accessed read-only, which the headers are. Things an attacker want to overwrite: * code * pointers to code * vtable pointers (pointers to pointers to code) * data that is evaluated by jump tables (index to pointers to code) The more direct the control, the better. For buffer writes, we usually have words that take addr len instead of just addr as in libc (with a bunch of recent replacement words which do take a length). The problem is that the buffers itself don't keep the length, especially with BUFFER: we screwed up: It should really return an addr len pair, so that e.g. 256 buffer: line ... line fd read-line throw would be straight-forward. Instead, you have to write line 256 fd read-line throw and then maybe you run into RAM pressure, decide that 128 bytes is enough for a line, but forget to adjust the code that uses read-line. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-03-02 10:04 +0000 |
| Message-ID | <2016Mar2.110423@mips.complang.tuwien.ac.at> |
| In reply to | #45760 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Sat, 27 Feb 2016 15:31:05 +0000 schrieb Anton Ertl:
>
>> Bernd Paysan <bernd.paysan@gmx.de> writes:
>>>Am Sat, 27 Feb 2016 07:15:05 +0000 schrieb Anton Ertl:
>>>Having a dictionary layout similar to a Flash system (write-once code in
>>>one segment, write-many buffers and variables in another segment) could
>>>prevent accidential overwriting of code.
>>
>> I am not sure that accidental overwriting of data is better (even if it
>> is not code addresses).
>
>Depends on the data. You want to overwrite deferred words or similar to
>get remote execution.
What I meant is that, for *accidental* overwrites, overwriting code
may be preferable because it probably causes a crash, while
accidentally overwriting data is more likely to go undetected and
maybe deliver wrong results.
For attacks, any arbitrarily deep indirect reference to code that is
used later for executing the code is an attack vector. Also, if you
can overwrite the address of a memory location that will be stored to
later, and also overwrite the data that will be stored there, you now
have an arbitrary write in writable memory, so that opens up all
memory to your attack (including separate return stack or other
separation attempts).
>Things an attacker want to overwrite:
>
>* code
>* pointers to code
>* vtable pointers (pointers to pointers to code)
>* data that is evaluated by jump tables (index to pointers to code)
* pointers to pointers to pointers to code (e.g., an object address)
* pointers to pointers to pointers to pointers to code
* ...
* Addresses for data writes where the attacker can also determine the data
>For buffer writes, we usually have words that take addr len instead of
>just addr as in libc (with a bunch of recent replacement words which do
>take a length).
Well, MOVE takes one length. Do you use the buffer length, or the
source length. Probably the latter, hopefully after checking that it
fits into the buffer. It sure is better than strcpy(), but it's far
from perfect.
> The problem is that the buffers itself don't keep the
>length, especially with BUFFER: we screwed up: It should really return an
>addr len pair, so that e.g.
>
>256 buffer: line
>
>...
>line fd read-line throw
>
>would be straight-forward. Instead, you have to write
>
>line 256 fd read-line throw
>
>and then maybe you run into RAM pressure, decide that 128 bytes is enough
>for a line, but forget to adjust the code that uses read-line.
So always passing the buffer length on the stack, and supplying words
that always push the buffer length, and also providing words that
consume the buffer-length (say, BMOVE ( afrom ufrom ato uto -- ior ))
would be one option, but would increase the stack clutter.
Some alternatives I have thought about:
1) Buffers always come with their length in the cell right before the
start. Then we would have BMOVE ( afrom ufrom buffer -- ior ), and
BMOVE would always fetch the length and check it. Likewise, we would
get BREAD-LINE ( buffer fid -- u ior ). Disadvantage: We cannot, say,
BMOVE into the middle of a buffer, so we would have to fall back to
the MOVE for that, or alternatively have a word where we supply a
buffer and an offset from the start of the buffer as target.
2) To work around the middle-of-the-buffer problem, we could have
buffer descriptors in memory consisting of start address and length of
the buffer we want to write into, and words for getting descriptors of
buffer parts out of existing buffers. Disadvantage: The additional
descriptors have to be memory-managed, which is cumbersome.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Doug Hoffman <glidedog@gmail.com> |
|---|---|
| Date | 2016-03-02 08:41 -0500 |
| Message-ID | <56d6eda8$0$293$14726298@news.sunsite.dk> |
| In reply to | #45776 |
On 3/2/16 5:04 AM, Anton Ertl wrote: > So always passing the buffer length on the stack, and supplying words > that always push the buffer length, and also providing words that > consume the buffer-length (say, BMOVE ( afrom ufrom ato uto -- ior )) > would be one option, but would increase the stack clutter. > > Some alternatives I have thought about: > > 1) Buffers always come with their length in the cell right before the > start. Then we would have BMOVE ( afrom ufrom buffer -- ior ), and > BMOVE would always fetch the length and check it. Likewise, we would > get BREAD-LINE ( buffer fid -- u ior ). Disadvantage: We cannot, say, > BMOVE into the middle of a buffer, so we would have to fall back to > the MOVE for that, or alternatively have a word where we supply a > buffer and an offset from the start of the buffer as target. > > 2) To work around the middle-of-the-buffer problem, we could have > buffer descriptors in memory consisting of start address and length of > the buffer we want to write into, and words for getting descriptors of > buffer parts out of existing buffers. Disadvantage: The additional > descriptors have to be memory-managed, which is cumbersome. I have used something like what you describe for a long time. For example I knew the following would be "safe" and work first-time because I have used the code for years: string+ buffer s" DataForABuffer" buffer !: 5 buffer end: ! \ set the insert: location s" INSERT-THIS-INTO-BUFFER" buffer insert: buffer p: \ print it \ => DataFINSERT-THIS-INTO-BUFFERorABuffer buffer free: The 'end: !' operation is not safe but easily could be made so. Objects are one way, obviously not the only way, to approach the issue. But objects are convenient, consistent in use, and minimize extra words. Any object system should suffice. Ref: FMS-SI (single inheritance) library code located here: http://soton.mpeforth.com/flag/fms/index.html include FMS-SI.f include FMS-SILib.f -Doug
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-03-02 23:22 +0000 |
| Message-ID | <nb7sj5$5mi$1@dont-email.me> |
| In reply to | #45776 |
Am Wed, 02 Mar 2016 10:04:23 +0000 schrieb Anton Ertl: > Well, MOVE takes one length. Do you use the buffer length, or the > source length. Probably the latter, hopefully after checking that it > fits into the buffer. It sure is better than strcpy(), but it's far > from perfect. Yes, interesting. Checking net2o, where I really care about not accidentally overwriting buffers, I either use the buffer length, *or* I use something like <dest> swap <size> umin move. That bmove might be a good factor for the latter case, so I could just say <dest> <size> bmove, with : bmove ( addr1 u1 addr2 u2 -- ) rot umin move ; bmove or ?move could also throw an error if the size isn't ok (I prefer throwing exceptions to returning iors - if you actually want to handle the exception, CATCH them. The idiomatic use of ior returned is to append a THROW to every word returning an ior.). -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | humptydumpty <ouatubi@gmail.com> |
|---|---|
| Date | 2016-03-02 22:57 -0800 |
| Message-ID | <4d35d058-cac8-427b-923a-f420658b16c1@googlegroups.com> |
| In reply to | #45791 |
On Thursday, March 3, 2016 at 1:25:17 AM UTC+2, Bernd Paysan wrote: > Am Wed, 02 Mar 2016 10:04:23 +0000 schrieb Anton Ertl: > > > Well, MOVE takes one length. Do you use the buffer length, or the > > source length. Probably the latter, hopefully after checking that it > > fits into the buffer. It sure is better than strcpy(), but it's far > > from perfect. > > Yes, interesting. Checking net2o, where I really care about not > accidentally overwriting buffers, I either use the buffer length, *or* I > use something like <dest> swap <size> umin move. That bmove might be a > good factor for the latter case, so I could just say <dest> <size> bmove, > with > > : bmove ( addr1 u1 addr2 u2 -- ) rot umin move ; > > bmove or ?move could also throw an error if the size isn't ok (I prefer > throwing exceptions to returning iors - if you actually want to handle > the exception, CATCH them. The idiomatic use of ior returned is to > append a THROW to every word returning an ior.). > > -- > Bernd Paysan > "If you want it done right, you have to do it yourself" > net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* > http://bernd-paysan.de/ Hi! I use something similar, as an adjective to 'move': \ prepare to put string(ca1,u1) into a buffer(ca2,u2) \ at left respective right. \ uses: left move or right move : left ( ca1 u1 ca2 u2 -- ca1 ca2 min[u1,u2] ) rot umin ; : right ( ca1 u1 ca2 u2 -- ca1 ca2+u2-u1 u1 ) rot tuck - dup 0 <= if + else rot + swap then ; : center ( ca1 u1 ca2 u2 -- ca1 ca2+[u2-u1]/2 u1 ) rot tuck - dup 0 <= if + else 2/ rot + swap then ; Enjoying Forth, humptydumpty
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-03-03 13:50 +0000 |
| Message-ID | <2016Mar3.145008@mips.complang.tuwien.ac.at> |
| In reply to | #45791 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Wed, 02 Mar 2016 10:04:23 +0000 schrieb Anton Ertl:
>
>> Well, MOVE takes one length. Do you use the buffer length, or the
>> source length. Probably the latter, hopefully after checking that it
>> fits into the buffer. It sure is better than strcpy(), but it's far
>> from perfect.
>
>Yes, interesting. Checking net2o, where I really care about not
>accidentally overwriting buffers, I either use the buffer length, *or* I
>use something like <dest> swap <size> umin move. That bmove might be a
>good factor for the latter case, so I could just say <dest> <size> bmove,
>with
>
>: bmove ( addr1 u1 addr2 u2 -- ) rot umin move ;
>
>bmove or ?move could also throw an error if the size isn't ok (I prefer
>throwing exceptions to returning iors - if you actually want to handle
>the exception, CATCH them. The idiomatic use of ior returned is to
>append a THROW to every word returning an ior.).
Yes. The Forth standard does iors because exceptions are optional,
and Gforth does iors for primitives, because throwing from a primitive
is complicated, but if we don't have such restrictions, the integrated
THROW is probably a good idea.
So it would be (untested)
: bmove ( a-from u-from a-to u-to -- )
rot 2dup u< if
umin move -11 throw
else
nip move
then ;
This performs the allowed part of the BMOVE even in the THROW case,
because if the user wants to progress afterwards, that may be what he
wants, and if he has to do it himself, he runs into the issues that
BMOVE is designed to avoid.
The case appears simpler for words like READ-FILE, but even there we
may have the case that the data size we want to read is different from
the buffer size, and then we get similar problems as with MOVE.
Even for a case where the buffer is treated as read-only (e.g., in
WRITE-FILE), we may want to deal with the buffer size in addition to
the transfer size; this might have prevented Heartbleed.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-03-04 21:01 +0000 |
| Message-ID | <nbct41$t6o$3@dont-email.me> |
| In reply to | #45803 |
Am Thu, 03 Mar 2016 13:50:08 +0000 schrieb Anton Ertl: > So it would be (untested) > > : bmove ( a-from u-from a-to u-to -- ) > rot 2dup u< if > umin move -11 throw > else > nip move > then ; Mostly, I would throw -9 (invalid memory access), and do DROP instead of UMIN (we know u-to is smaller, so we use this fact* to optimize; unlike using "facts", this is allowed ;-). That's what you would expect for an out-of-bound access protected by a read-only page boundary, so this is IMHO the most meaningful result. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-03-06 10:55 +0000 |
| Message-ID | <2016Mar6.115507@mips.complang.tuwien.ac.at> |
| In reply to | #45841 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Thu, 03 Mar 2016 13:50:08 +0000 schrieb Anton Ertl:
>
>> So it would be (untested)
>>
>> : bmove ( a-from u-from a-to u-to -- )
>> rot 2dup u< if
>> umin move -11 throw
>> else
>> nip move
>> then ;
>
>Mostly, I would throw -9 (invalid memory access),
I expect -9 to come from a hardware exception, so I would rather
prefer something else. Of course, in Gforth you will see the
backtrace and infer the problem from that, but that's despite the -9,
not because of it. -11 may not be so great, either, maybe we need a
new one.
> and do DROP instead of UMIN
Yes.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> |
|---|---|
| Date | 2016-02-27 11:31 -0500 |
| Message-ID | <20160227113138.5f8aa6da@_> |
| In reply to | #45717 |
On Sat, 27 Feb 2016 07:15:05 GMT anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote: > Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> writes: > >Buffer overflows work in C because C uses a combined data stack and > >return stack, i.e., stack frame. > > No. Yes, that's why such attacks work in C, going all the way back to the original Morris worm. > Separating the return addresses is not sufficient to preventing > arbitrary code execution. True, but that wasn't in the scope of your original post. > If return addresses were separate, attackers would overwrite > function pointers. Yes, if they can find them. The entire reason that overflow attacks are used is because: 1) they're easy, and 2) they work Searching for pointers to functions in code, which may or may not be over-writable, and which may or may not be executable, isn't easy. Accessing a stack with control-flow data or saved return address via a register or simple function, such as the return stack in Forth or a stack frame for most C implementations, is much easier. > >Forth words in the dictionary, while > >they may contain an XT, are not buffers that can be written to > >and overflowed via input and print string functions. > > Counterexample: > > create buf 100 allot > create two-xts ' . , ' . , > buf 1000 untrusted-file read-file Then, you may need to separate buffer space and/or variable space from code space and (fixed) data space. variable space.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-02-27 17:14 +0000 |
| Message-ID | <2016Feb27.181432@mips.complang.tuwien.ac.at> |
| In reply to | #45721 |
Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> writes:
>On Sat, 27 Feb 2016 07:15:05 GMT
>anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
>
>> Rod Pemberton <NoHaveNotOne@bcczxcfre.cmm> writes:
>
>> >Buffer overflows work in C because C uses a combined data stack and
>> >return stack, i.e., stack frame.
>>
>> No.
>
>Yes, that's why such attacks work in C, going all the way
>back to the original Morris worm.
>
>> Separating the return addresses is not sufficient to preventing
>> arbitrary code execution.
>
>True, but that wasn't in the scope of your original post.
Everything one can do with a buffer overflow attack was in the scope
of my original posting; I mentioned return-address stuff as an
example, but an example does not define the scope (at least not for
me).
>> If return addresses were separate, attackers would overwrite
>> function pointers.
>
>Yes, if they can find them. The entire reason that overflow
>attacks are used is because:
>
>1) they're easy, and
>2) they work
>
>Searching for pointers to functions in code, which may or may
>not be over-writable, and which may or may not be executable,
>isn't easy. Accessing a stack with control-flow data or saved
>return address via a register or simple function, such as the
>return stack in Forth or a stack frame for most C implementations,
>is much easier.
The function pointers are in the data, and finding one is as easy as
finding a return address: Once you have found an unprotected buffer,
just trace all the memory reads from the area behind the buffer, and
if one of them (directly or indirectly) leads to an indirect branch
(whether plain indirect jump, indirect call, or a return), that's your
point of entrance.
>Then, you may need to separate buffer space and/or variable space
>from code space and (fixed) data space.
Now what if you have an array containing xts? Where do you put it?
More and more complicated separation regimes make things a little
harder for the attacker, but they tend to work around such measures.
I think it's better to avoid the buffer overflow in the first place.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2016-02-27 18:59 +0200 |
| Message-ID | <naskf0$tts$1@dont-email.me> |
| In reply to | #45707 |
On 26/02/2016 15:38, Anton Ertl wrote: > A frequent source of vulnerabilities in C is buffer overflows: Not just C... > So what else can we do? The idea I am going to explore here is to > limit the part where you have to take special care to a small part of > the program; that goes nicely with the idea that you write an > application-specific wordset and use that to program the application. I realize you're taking about ANS Forth, but this category of problems is one which I've worked on eliminating in 8th's design. The solutions used there won't necessarily apply to Forth, but I'll mention for your consideration: The user is never* given direct access to memory (* except via the FFI, which remains and will probably always remain an Achilles heel). So it is impossible to poke values into arbitrary memory. The return stack is not accessible at all. The "r@" etc words use an auxiliary stack and have nothing to do with the return stack. So it's not possible to subvert the return stack by overwriting somehow with the "r" words. String/buffer/array access cannot ever access beyond the limits of the item (or, in the case of an array, simply grows the array). So it is impossible to create a buffer and write off the end of it. There is no facility for in-memory assembly (because you cannot poke bytes into executable memory). Again: I realize these are not going to be useful to ANS Forth, but perhaps they can give some ideas of things which might be explored in other ways?
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-02-27 17:04 +0000 |
| Message-ID | <2016Feb27.180447@mips.complang.tuwien.ac.at> |
| In reply to | #45722 |
Ron Aaron <rambamist@gmail.com> writes:
>On 26/02/2016 15:38, Anton Ertl wrote:
>> So what else can we do? The idea I am going to explore here is to
>> limit the part where you have to take special care to a small part of
>> the program; that goes nicely with the idea that you write an
>> application-specific wordset and use that to program the application.
>
>I realize you're taking about ANS Forth, but this category of problems
>is one which I've worked on eliminating in 8th's design. The solutions
>used there won't necessarily apply to Forth, but I'll mention for your
>consideration:
>
>The user is never* given direct access to memory
That is actually more or less the idea I explored in my posting,
except that I allow the programmer to perform classic Forth memory
accesses in INSECURE code, and there is no effort to prevent the
programmer from intentionally subverting the SECURE regime.
>(* except via the FFI,
>which remains and will probably always remain an Achilles heel).
Yes, that's one point I thought about mentioning, but didn't.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2015: http://www.rigwit.co.uk/EuroForth2015/
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.forth
csiph-web