Pojęcia "przepełnienie stosu" oraz "przepełnienie buforu na stosie" są dość często używane zamiennie. Niestety, jest to nieprawidłowe - są to dwie zupełnie różne klasy bugów, których nie należy ze sobą mylić. Niniejszy post ma na celu wykazanie różnicy, a także luźne rozważania na temat genezy błędu.

Shellcoders Handbook PL, wyd. 1
"The Shellcoder's Handbook, Edycja Polska" (2004, wydanie 1, wydawnictwo Helion)

Zacznę może od zdefiniowania co to jest "przepełnienie bufora na stosie" [eng. stack-based buffer overflow], a co "przepełnienie stosu" [eng. stack overflow lub stack exhaustion] - tak żebyśmy byli "zsynchronizowani".

Przepełnienie bufora na stosie

Klasyczny i jednocześnie popularny błąd opisany w Smashing The Stack For Fun And Profit przez Aleph One - czyli mamy bufor znajdujący się na stosie, jest do niego kopiowane za dużo danych, co powoduje nadpisanie danych znajdujących się bezpośrednio za buforem (klasycznie jest to oczywiście adres powrotu), a w rezultacie crash lub arbitrary code exec.

Jeśli chodzi o przykłady błędów tego typu, to jest ich masa. Niech więc jako przykład posłużą dwa, którym się trochę bardziej przyglądałem:
- w 2007 po sieci "grasował" malware z 0-day'em exploitującym loader animowanych kursorów myszy pod Vistą,
- w tym samym roku j00ru doszukał się takowego błędu w Gadu-Gadu

Przepełnienie stosu

Mniej spotykany (przynajmniej w security/hackingu) błąd polegający na przepełnieniu stosu, tj. wyczerpaniu ilości miejsca zarezerwowanego na stos i próba pisania/czytania z pamięci, która leży dalej w kierunku rośnięcia stosu (współczesne systemy operacyjne mają tam zazwyczaj separator w postaci zarezerwowanej ale nie zaalokowanej, lub wręcz nie alokowalnej, strony pamięci).

Zazwyczaj do takich sytuacji dochodzi w przypadku:
- nadmiernej lub wręcz nieograniczonej rekurencji (itp.)
- próby alokacji zbyt dużego bufora na stosie (patrz również: C i lokalne tablice o "zmiennej" wielkości)

Jeśli chodzi o przykłady błędów tego typu, to można wskazać np. na:
- błąd w Xorg znaleziony przez Rafała Wojtczuka z ITL (2010)
- a także na artykuł Exploiting Stack Overflows in the Linux Kernel by Jon Oberheide (thx mak za linka)

The difference?

Tak na wszelki wypadek zapiszę różnicę explicit:
- W "przepełnieniu bufora na stosie" przepełniany jest bufor, który w tym akurat wypadku znajduje się na stosie.
- Natomiast, w "przepełnieniu stosu" przepełniany jest stos.

Skrócona wersja "przepełnienia bufora na stosie"?

Może najpierw rzućmy okiem jak to jest w przypadku oryginału (biorąc pod uwagę ekonomizację języka, czyli nasze dążenie do skracania długich wyrażeń).
- Punktem wyjście jest długawe "stack-based buffer overflow" (od którego zaczął Aleph One).
- Usuwamy "buffer" (w końcu z kontekstu wynika o co chodzi) i zostaje "stack-based overflow".
- Ewentualnie, usuwamy "-based" i dostajemy stack buffer overflow". Ten oraz powyższy sposób można zauważyć w użyciu np. na angielskojęzycznej Wikipedii. Dodam, że oba są jednoznaczne (tj. jednoznacznie wskazują na klasę błędów).
- Usunięcie zarówno "-based" i "buffer", prowadzi niestety do wieloznacznego "stack overflow" (a raczej do "stack overflow", które określa już inną klasę błędów). [I tu pytanie do eksperta od języka angielskiego - czy można tutaj uznać, że implicit podmiot pozostaje bez zmian? jeśli tak, to można założyć, że jest to poprawne, mimo że niejednoznaczne.]
- W sumie można by też pisać "overflow on stack", co wydaje się być jednoznaczne w tym kontekście.
- No i zawsze można pisać "stack BO" :)
Dodam, że z uwagi na tą wieloznaczność "stack overflow", w języku angielskim stosuje się również termin "stack exhaustion" na określenie przepełnienia stosu (o czym zresztą wspomniałem na górze postu). Polskiego odpowiednika niestety brak - Google pokazuje 8 wyników na "wyczerpanie stosu", i to w kapkę innych kontekstach.

A teraz wersja polska:
- Punktem wyjścia jest przydługawe "przepełnienie bufora na stosie".
- Możemy usunąć "bufora", dzięki czemu dostaniem jednoznaczne "przepełnienie na stosie" (analogiczne do angielskiego "stack-based overflow").
- A gdyby tak usunąć jeszcze "na"... Niestety, "przepełnienie stosie" jest niepoprawne gramatycznie (i w ogóle brzmi dziwnie ;p). Natomiast są dwie inne drogi:
-- Zmieniamy "na stosie" na "(jakie?) stosowe" dostając jednoznaczne "przepełnienie stosowe".
-- Opcją drugą jest nasz dzisiejszy "culprit", czyli zamiana "na stosie" na "(czego?) stosu". Zauważcie, że z punktu widzenia logiki zdania zmienia się podmiot - w oryginale był nim implicit bufor, a w otrzymanej wersji jest nim explicit stos. Tak więc ta wersja do określenia "przepełnienia buforu na stosie" jest po prostu niepoprawna.

Czemu więc "przepełnienie stosu" jest używane do określenia "przepełnienia na stosie"? Osobiście stawiam na błąd w tłumaczeniu (lub powielanie błędów przy tłumaczeniu) - tj. angielskojęzyczny autor napisał stack overflow, a tłumacz grzecznie przetłumaczył na przepełnienie stosu (zamiast np. przepełnienie stosowe or sth).

UPDATE: Spotkałem się z jeszcze jednym tłumaczeniem: "przepełnienie bufora stosu". Niestety, jest ono również niepoprawne, ponieważ sugeruje, że bufor należy do stosu ("bufor" kogo/czego? "stosu"), a więc, że stos (dostęp do niego?) jest w pewien sposób buforowany, i to właśnie przepełnienie tego bufora stosu jest problemem. Oczywiście, stos zazwyczaj jest buforowany (cache CPU), natomiast na aktualny stan wiedzy nie idzie wykorzystać jego przepełnienia w sposób podobny do "przepełnienia bufora na stosie" (do głowy przychodzą mi jedynie jakieś timing attacks związane z cache miss'ami). Podsumowując, "przepełnienie bufora stosu" użyte do opisania "stack-based buffer overflow" jest niepoprawne, ponieważ opisuje coś zupełnie innego (a także: bufor po prostu leży na stosie, ale nie należy do stosu).


I tyle ;>

Inne źródła

Kilka dodatkowych linków w tym temacie:

* Stack overflow (stack exhaustion) not the same as stack buffer overflow:
We wanted to clarify the distinction between stack exhaustion and stack buffer overflow. Stack buffer overflows often lead to elevation of privilege. Unfortunately, the literature tends to use stack overflow to refer to both cases, hence the confusion. The error code STATUS_STACK_BUFFER_OVERRUN (0xc0000409) refers to a stack buffer overflow while the error code STATUS_STACK_OVERFLOW (0xc00000fd) refers to stack exhaustion.
* CWE-121: Stack-based Buffer Overflow
"Stack Overflow" is often used to mean the same thing as stack-based buffer overflow, however it is also used on occasion to mean stack exhaustion, usually a result from an excessively recursive function call. Due to the ambiguity of the term, use of stack overflow to describe either circumstance is discouraged.
* Wikipedia (EN) - Buffer overflow - Stack-based exploitation
Stack-based buffer overflows are not to be confused with stack overflows.

Comments:

2011-12-25 09:03:08 = XANi
{
Czyli w skrócie każdy "stack-based buffer overflow" to "stack overflow" ale nie każdy "stack overflow" to "stack-based buffer overflow"?
}
2011-12-25 10:25:51 = Gynvael Coldwind
{
@XANi
W skrócie - to oddzielne klasy błędów, które niestety są ze sobą mylone (z uwagi na to, że wygodnie było skrócić nazwę jednej do drugiej).
Technicznie "stack-based buffer overflow" to nie "stack overflow". Językowo... cóż, to by trzeba zapytać anglisty.
}
2011-12-28 09:53:03 = Karton
{
Skoro wielkość strony pamięci jest określona, to co dają te separatory we współczesnych systemach?
Nie można wywołując błąd, powiększyć argumentu o rozmiar tej strony?

Tak, wiem, że czegoś nie rozumiem :)
}
2011-12-28 11:05:46 = F
{
Bardzo fajny artykuł, uświadomił mi, że aby nie dochodziło do nieporozumienia lepiej stosować pełne nazwy :). (takie podejście też brzmi bardziej profesjonalnie)
}
2012-01-09 22:32:02 = Hern
{
To ja może przy okazji tego, że został wspomniany stack overflow opiszę sytuację kiedy zetknąłem się z tym błędym. Otóż klasa A miała publiczną zmienną reprezentującą jakąś zewnętrzną klasę do pisania do pliku(Foo m_foo). Zmienna ta byłą inicjowana(inicjowana czy inicjalizowana? temat na kolejny wpis? :P) przez przekazanie obiektu tej klasy do konstruktora. Klasa B dziedziczyła A. Zmienna typu Foo była przekazywana do konstruktra B a on następnie wywoływał konstruktor A z tą zmienną. Stack overlfowa następował od razu po wejściu do jednej z metod B. Przyczyną okazało się niezaincjowanie m_foo, bo konstruktor wyglądał mniej więcej tak B(Foo zmienna) : A(m_foo). Błąd głupi, ale trochę się go naszukałem i znalazłem go dopiero kiedy po zwiększeniu rozmiaru stosu w opcjach visuala program wywalał się kilka liniejek dalej i pod debuggerem dostrzegłem tę nizaincjowaną zmienną :). Pozdrawiam :)
}
2012-12-11 00:35:56 = marcin
{
@Hern
Initiation -> Initiation is a rite of passage ceremony marking entrance or acceptance into a group or society
Initialization -> In computer programming, initialization is the assignment of an initial value for a data object or variable.

Zdecydowanie inicjalizacja, inicjalizowanie...
:)
}
2012-12-11 07:18:36 = Gynvael Coldwind
{
@marcin
Ostrożnie z wnioskowaniem o języku polskim na podstawie definicji angielskich słów (można się na false friendsach przejechać).
Ad inicjacja vs inicjalizacja: http://poradnia.pwn.pl/lista.php?id=9919

Niemniej jednak osobiście też preferuje "inicjalizacje".
}

Add a comment:

Nick:
URL (optional):
Math captcha: 2 ∗ 3 + 10 =