programing

alloca()의 사용은 왜 베스트 프랙티스로 간주되지 않습니까?

shortcode 2022. 8. 30. 22:48
반응형

alloca()의 사용은 왜 베스트 프랙티스로 간주되지 않습니까?

alloca()메모리 할당은 다음과 같이 히프가 아닌 스택에 이루어집니다.malloc()그래서 일상에서 돌아오면 기억은 자유로워진다.이것에 의해, 동적으로 할당된 메모리를 해방하는 문제가 해결됩니다.를 통해 할당된 메모리의 해방malloc()주요 골칫거리입니다. 만약 놓치면 모든 종류의 기억장애로 이어집니다.

의 사용은 무엇입니까?alloca()위의 특징에도 불구하고 낙담하고 있습니까?

정답은 바로 저기에 있습니다.man페이지(Linux 이상):

RETURN VALUE alloca() 함수는 할당된 공간의 선두로 포인터를 반환합니다.할당으로 인해 스택 오버플로가 발생할 경우 프로그램 동작은 정의되지 않습니다.

절대 사용하지 말라는 말은 아닙니다.제가 작업하는 OSS 프로젝트 중 하나가 광범위하게 사용되고 있는데, 이를 악용하지 않는 한(alloca'거대한 가치를 지닌다'는 거죠"few 100 bytes" 마크를 넘으면 다음 단계로 넘어갑니다.malloc대신 친구들이요.할당 실패는 계속 발생할 수 있지만 스택을 날려버리는 것이 아니라 최소한 실패 징후가 나타납니다.

제가 가장 기억에 남는 버그 중 하나는 다음과 같은 인라인 함수에 관한 것입니다.alloca프로그램 실행 중 임의의 지점에서 스택 오버플로로 나타납니다(스택에 할당되기 때문입니다).

헤더 파일:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

구현 파일:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

컴파일러가 인라인을 통해DoSomething기능 및 모든 스택 할당이 내부에서 이루어졌습니다.Process()스택을 폭파합니다.변명을 하자면 (문제를 발견한 것은 저뿐만이 아닙니다.해결할 수 없을 때는 상급 개발자에게 가서 울어야 했습니다)그것은 분명하지 않았습니다.allocaATL 문자열 변환 매크로 중 하나였습니다.

그래서 교훈은 '사용하지 말라'는 것이다.alloca함수에 포함시킬 수 있습니다.

오래된 질문이지만 가변 길이 배열로 대체해야 한다는 언급은 없었습니다.

char arr[size];

대신

char *arr=alloca(size);

표준 C99에 포함되어 있으며 많은 컴파일러에서 컴파일러 확장으로 존재했습니다.

alloca()는 표준 로컬 변수를 사용할 수 없는 경우에 매우 유용합니다. 왜냐하면 alloca()에서 얻은 포인터는 실행 시 결정되어야 하며 이 함수가 반환된 에는 절대 사용되지 않음을 보장할 수 있기 때문입니다.

안전할 수 있습니다.

  • 포인터 또는 포인터가 포함된 어떤 것도 반환하지 마십시오.
  • 힙에 할당된 구조에 포인터를 저장하지 않음
  • 다른 스레드에서 포인터를 사용하지 않도록 합니다.

진짜 위험은 다른 누군가가 나중에 이러한 조건을 위반할 가능성에서 온다.이러한 점을 염두에 두고 텍스트를 포맷하는 함수에 버퍼를 전달하면 매우 편리합니다.

아무도 이것에 대해 언급하지 않은 것 같다.함수에 alloca를 사용하면 함수의 스택 프레임 크기를 컴파일러가 알 수 없기 때문에 함수에 적용될 수 있는 최적화를 방해하거나 비활성화합니다.

예를 들어, C 컴파일러에 의한 일반적인 최적화는 함수 내에서 프레임 포인터의 사용을 배제하는 것입니다.프레임 액세스는 스택 포인터에 대해 상대적으로 이루어집니다.따라서 일반적인 사용을 위한 레지스터가 하나 더 있습니다.그러나 함수 내에서 alloca가 호출되면 함수의 일부에 대해 sp와 fp의 차이를 알 수 없으므로 이 최적화를 수행할 수 없습니다.

컴파일러의 사용의 희귀성과 표준 함수로서의 불명확한 상태를 고려할 때, 컴파일러 설계자는 할당에 문제를 일으킬 수 있는 최적화를 무효로 할 가능성이 매우 높습니다.

업데이트: 가변 길이의 로컬 어레이가 C에 추가되어 있으며, 이들 어레이는 컴파일러에 할당과 유사한 코드 생성 문제를 일으키기 때문에 기본 메커니즘에는 "사용의 드물고 의심스러운 상태"가 적용되지 않는 것으로 알고 있습니다.그러나 여전히 할당 또는 VLA를 사용하면 함수 내에서 코드 생성을 손상시킬 수 있습니다.사용할 수 있습니다.컴파일러 디자이너의 피드백을 환영합니다.

이 "오래된" 질문에 대한 많은 흥미로운 답변, 심지어 비교적 새로운 답변도 있지만, 저는 이 질문에 대해 언급하는 것을 발견하지 못했습니다.

적절하게 주의하여 사용하는 경우alloca()(어플리케이션 전체에 걸쳐) 작은 가변 길이 할당(또는 C99 VLA(사용 가능한 경우)을 처리함으로써 고정 길이의 오버사이즈 로컬 어레이를 사용하는 경우에 비해 전체적인 스택 증가를 줄일 수 있습니다.그렇게alloca()신중하게 사용하면 스택도움이 될 수 있습니다.

나는 그 인용문을…에서 찾았다.네, 제가 그 인용문을 만들었어요.하지만 정말 생각해 보세요...

@j_discripts_discripts는 다른 답변 아래 그의 코멘트에서 매우 옳다.의 사용을 회피하다alloca()크기가 큰 로컬 어레이를 선호하면 스택 오버플로우로부터 프로그램을 안전하게 할 수 없습니다(컴파일러가 다음 기능을 사용할 수 있을 만큼 오래되지 않은 경우).alloca()업그레이드 할 필요가 있는 경우 또는 를 사용하지 않는 한alloca()내부 루프, 이 경우에는...사용하지 않다alloca()내부 루프).

저는 데스크탑/서버 환경 및 임베디드 시스템에 종사해 왔습니다.많은 임베디드 시스템에서는 힙을 전혀 사용하지 않습니다(지원 링크도 하지 않습니다).다이나믹하게 할당된 메모리는, 동시에 몇년간 재기동하지 않는 애플리케이션의 메모리 누수의 리스크에 의해서 악영향을 받는다는 인식이나, 다이나믹 메모리가 위험하다고 하는 보다 합리적인 이유가 있습니다.애플리케이션이 잘못된 메모리가 소진될 때까지 힙을 프래그먼트화하지 않는 것을 확실히 알 수 없습니다.따라서 임베디드 프로그래머에게는 대안이 거의 없습니다.

alloca()(또는 VLA)는 업무에 적합한 도구일 수 있습니다.

나는 프로그래머가 스택에 할당된 버퍼를 "가능성이 있는 모든 경우를 처리할 수 있을 만큼 충분히 큰" 버퍼로 만드는 것을 다시 보았다.깊이 중첩된 콜트리에서는 그 (안티?)패턴을 반복적으로 사용하면 스택 사용이 과장됩니다.(콜트리의 깊이가 20레벨로 되어 있습니다.각 레벨에서는 일반적으로 이 함수는 1024바이트의 버퍼를 16바이트 이하로만 사용하고 극히 드문 경우에만 "안전하기 위해서"를 무턱대고 할당합니다.)대안은 다음과 같습니다.alloca()또는 VLA를 사용하여 기능에 필요한 만큼의 스택스페이스만 할당함으로써 불필요한 부하를 회피할 수 있습니다.콜 트리 내의 1개의 함수가 통상보다 큰 할당을 필요로 하는 경우 콜트리 내의 다른 함수는 여전히 통상적인 작은 할당을 사용하고 있으며, 전체 애플리케이션스택 사용률은 모든 함수가 로컬버퍼를 무작정 오버할당했을 때보다 현저하게 낮아집니다.

하지만 만약 당신이 그것을 사용하기로 선택한다면alloca()...

이 페이지의 다른 답변에 따르면 VLA는 안전하다고 생각됩니다(루프 내에서 호출된 경우 스택 할당이 복합되지 않음). 단, VLA를 사용하는 경우alloca()루프내에서 사용하지 않도록 주의해 주세요.또, 다른 함수의 루프내에서 호출될 가능성이 있는 경우는, 인라인을 할 수 없게 해 주세요.

뉴스그룹 투고에서 언급했듯이,alloca어렵고 위험한 것으로 간주될 수 있습니다.

  • 일부 컴파일러는 지원하지 않습니다.alloca.
  • 일부 컴파일러는 의도된 동작을 해석합니다.alloca그 때문에, 그것을 서포트하는 컴파일러간에서도 휴대성이 보증되지 않습니다.
  • 구현에 따라서는 버그가 발생합니다.

한 가지 문제는 이것이 널리 지지되고 있지만 표준이 아니라는 것입니다.다른 조건이 같다면 저는 항상 일반적인 컴파일러 확장자보다는 표준 함수를 사용합니다.

다른 답은 모두 정답입니다.단, 할당하는 것이alloca()상당히 작은 편이지만 사용하는 것보다 빠르고 편리한 기술이라고 생각합니다.malloc()또는 다른 방법으로.

바꿔 말하면alloca( 0x00ffffff )위험하고 오버플로를 일으킬 가능성이 있습니다.char hugeArray[ 0x00ffffff ];조심하고 합리적으로 행동하면 괜찮을 거야

한 번의 함정alloca그것이다longjmp되감습니다.

즉, 콘텍스트를 저장했을 경우setjmp,그리고나서alloca그럼, 어떤 기억?longjmp이 문맥에 따르면, 당신은 그 문맥을 잃어버릴 수 있습니다.alloca기억.스택 포인터가 원래 위치로 돌아왔기 때문에 함수를 호출하거나 다른 기능을 수행하는 경우 메모리가 예약되지 않습니다.alloca원본을 클로버합니다.alloca.

분명히 말씀드리면, 제가 여기서 구체적으로 말하는 것은longjmp이 함수는 이 함수로 복귀하지 않습니다.alloca일어났어!오히려 함수는 다음 명령어를 사용하여 컨텍스트를 저장합니다.setjmp; 다음으로 메모리를 할당합니다.alloca마지막으로 그 컨텍스트에 대해 longjmp가 발생합니다.그 기능은alloca메모리가 모두 해방된 것은 아닙니다.메모리가 해방된 이후 할당된 모든 메모리만setjmp물론 관찰된 동작에 대해 말하고 있습니다.이러한 요건은, 다음의 어느 것에 대해서도 문서화되어 있지 않습니다.alloca나도 알아

이 문서의 초점은 보통 다음과 같은 개념에 맞춰져 있습니다.alloca메모리는 어떤 블록도 아닌 기능 활성화와 관련지어져 있습니다.그 복수의 호출은alloca기능이 종료되면 모두 해방되는 스택메모리를 더 가져오기만 하면 됩니다.그렇지 않습니다.메모리는 실제로 프로시저 컨텍스트와 관련되어 있습니다.콘텍스트가 복원되는 경우longjmp이전 버전도 마찬가지입니다.alloca스택 포인터 레지스터 자체가 할당에 사용되고 (필요에 따라) 저장 및 복원된 결과입니다.jmp_buf.

덧붙여서, 만약 그렇게 동작한다면, 이것은 의도적으로 메모리를 해방하기 위한 그럴듯한 메커니즘을 제공합니다.alloca.

나는 이것을 버그의 근본 원인으로 마주쳤다.

alloca 사용은 여전히 권장되지 않습니다, 왜?

나는 그런 공감대를 느끼지 못한다.많은 장점; 몇 가지 단점:

  • C99는 가변 길이 어레이를 제공합니다.이는 고정 길이 어레이와 일관성이 높고 직관적인 전체적인 표기로 자주 사용됩니다.
  • 많은 시스템에서 스택에 사용할 수 있는 전체 메모리/주소 공간이 힙에 비해 적기 때문에 프로그램이 (스택 오버플로를 통해) 메모리 소진에 약간 더 취약합니다.이는 좋은 점 또는 나쁜 점으로 보일 수 있습니다.스택이 자동으로 확장되지 않는 이유 중 하나는 제어 불가능한 프로를 방지하기 위해서입니다.전체 기계에 미치는 악영향으로 인한 그램
  • 로컬 스코프(예:while또는for루프) 또는 여러 범위에서 메모리는 반복/반복마다 누적되며 함수가 종료될 때까지 해제되지 않습니다.이것은 제어 구조의 범위에서 정의된 일반 변수(예:for {int i = 0; i < 2; ++i) { X }축적되다alloca-ed 메모리는 X에서 요구되지만 고정 크기 어레이의 메모리는 반복마다 재사용됩니다).
  • 최신 컴파일러는 일반적으로inline호출하는 함수alloca하지만 당신이 그들을 강요한다면alloca발신자의 컨텍스트에서 발생합니다(즉, 발신자가 돌아올 때까지 스택이 해방되지 않습니다).
  • 오래 전에.alloca비휴대용 기능/해크에서 표준 확장 기능으로 전환되었지만 일부 부정적인 인식이 지속될 수 있습니다.
  • 수명은 함수 범위에 구속되며, 이는 프로그래머에게 더 적합할 수도 있고 그렇지 않을 수도 있습니다.malloc의 명시적 제어
  • 사용해야 하는 모양malloc는 할당 해제를 검토하도록 권장하고 있습니다.이것이 래퍼 기능을 통해 관리되고 있는 경우(예:WonderfulObject_DestructorFree(ptr)이 함수는 클라이언트 코드를 명시적으로 변경하지 않고 파일 기술자를 닫거나 내부 포인터를 해방하거나 로깅을 실행하는 등 구현 정리 작업을 수행할 수 있는 지점을 제공합니다.일관적으로 채택하는 것이 좋은 모델일 수 있습니다.
    • 이 의사 OO 스타일의 프로그래밍에서는, 다음과 같은 것을 원하는 것은 당연합니다.WonderfulObject* p = WonderfulObject_AllocConstructor();- "displor"가 반환되는 함수일 때 가능합니다.malloc-ed 메모리(함수가 저장할 값을 반환한 후 메모리가 할당된 상태로 유지됨)p). 단, "관리자"가alloca
      • 매크로 버전WonderfulObject_AllocConstructor이를 달성할 수 있지만, 서로 모순되는 코드와 모순되어 의도하지 않은 대체와 결과적으로 해결이 어려운 문제를 일으킬 수 있다는 점에서 "악"이다.
    • 실종된freeValGrind, Purify 등에 의해 조작을 검출할 수 있지만, 「파괴자」콜의 누락은 항상 검출할 수 있는 것은 아닙니다.사용 목적의 실시의 관점에서 보면, 매우 미미한 메리트가 있습니다.alloca()구현(GCC 등)은 인라인 매크로를 사용하여alloca()따라서 메모리 용량 진단 라이브러리의 런타임 대체는 다음과 같은 방법으로 불가능합니다.malloc/realloc/free(예: 전기 울타리)
  • 일부 구현에는 미묘한 문제가 있습니다. 예를 들어 Linux의 manpage에서 다음과 같은 문제가 있습니다.

    alloca()에 의해 예약된 스택공간이 함수 인수의 공간 가운데 스택에 나타나기 때문에 많은 시스템에서 함수 호출 인수 목록 내에서 alloca()를 사용할 수 없습니다.


이 질문이 C 태그로 되어 있는 것은 알지만, C++ 프로그래머로서 C++를 사용하여 C++의 잠재적인 유용성을 설명하려고 합니다.alloca: 아래 코드(및 여기 ideone)는 힙이 아닌 스택에 할당된(수명이 함수 반환에 연결된) 다양한 크기의 다형성 유형을 만듭니다.

#include <alloca.h>
#include <iostream>
#include <vector>

struct Base
{
    virtual ~Base() { }
    virtual int to_int() const = 0;
};

struct Integer : Base
{
    Integer(int n) : n_(n) { }
    int to_int() const { return n_; }
    int n_;
};

struct Double : Base
{
    Double(double n) : n_(n) { }
    int to_int() const { return -n_; }
    double n_;
};

inline Base* factory(double d) __attribute__((always_inline));

inline Base* factory(double d)
{
    if ((double)(int)d != d)
        return new (alloca(sizeof(Double))) Double(d);
    else
        return new (alloca(sizeof(Integer))) Integer(d);
}

int main()
{
    std::vector<Base*> numbers;
    numbers.push_back(factory(29.3));
    numbers.push_back(factory(29));
    numbers.push_back(factory(7.1));
    numbers.push_back(factory(2));
    numbers.push_back(factory(231.0));
    for (std::vector<Base*>::const_iterator i = numbers.begin();
         i != numbers.end(); ++i)
    {
        std::cout << *i << ' ' << (*i)->to_int() << '\n';
        (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                         // program depends on side effects of destructor
    }
}

프로세스에서 사용할 수 있는 스택스페이스의 양은 한정되어 있습니다.이는 사용 가능한 메모리 용량보다 훨씬 적은 양입니다.malloc().

사용방법alloca()스택 오버플로우 에러(행운의 경우는)가 발생할 가능성이 큰폭으로 높아집니다.불행의 경우는 설명할 수 없는 크래쉬가 발생합니다.

스택 오버플로에 의한 정의되지 않은 동작이라고 하는 큰 문제는 이미 모두 지적되어 있습니다만, Windows 환경에서는 구조화 예외(SEH)와 가드 페이지를 사용하여 이를 검출할 수 있는 훌륭한 메커니즘이 있습니다.스택은 필요한 경우에만 증가하므로 이러한 가드페이지는 할당되지 않은 영역에 배치됩니다.(스택을 오버플로하여) 할당하면 예외가 느려집니다.

이 SEH 예외를 검출하고 _resetstkoflw를 호출하여 스택을 리셋하고 즐거운 여행을 계속할 수 있습니다.이상적이지는 않지만 적어도 무언가가 잘못되었다는 것을 알 수 있는 또 다른 메커니즘입니다.*nix에는 제가 모르는 비슷한 것이 있을 수 있습니다.

alloca를 래핑하여 내부적으로 추적하여 최대 할당 크기를 제한할 것을 권장합니다.이 문제에 대해 진지하게 고민하는 경우 기능 범위 내의 할당을 추적하기 위해 기능 맨 위에 몇 가지 스코프 Sentry를 던져 프로젝트에 허용된 최대 용량과 비교하여 온전성을 확인할 수 있습니다.

또, 메모리 리크를 허가하지 않는 것 외에, alloca는 메모리 플래그멘테이션을 일으키지 않습니다.이것은 매우 중요합니다.alloca를 현명하게 사용한다면 나쁜 방법은 아니라고 생각합니다.이것은 기본적으로 모든 것에 해당됩니다. :-)

제 생각에 alloca()는 가능한 경우 제한적으로만 사용해야 합니다."goto"의 사용과 마찬가지로, 합리적인 사람들 중 상당수는 "alloca"의 사용뿐만 아니라 그 존재에 대해서도 강한 거부감을 가지고 있다.

스택 사이즈가 알려져 있고 할당 사이즈에 대한 규약과 분석을 통해 제한을 가할 수 있는 임베디드 사용, 컴파일러가 C99+를 지원하도록 업그레이드 할 수 없는 경우에는 alloca()를 사용해도 괜찮습니다.

사용 가능한 경우 VLA는 alloca()보다 몇 가지 이점이 있을 수 있습니다.컴파일러는 어레이 스타일액세스를 사용할 때 Out-Bounds 액세스를 포착하는 스택 제한 체크를 생성할 수 있습니다(컴파일러가 이것을 실행할지는 모르겠지만 할 수 있습니다).또, 코드를 분석하면, 어레이 액세스 식이 적절히 한정되어 있는지를 판별할 수 있습니다.자동차, 의료기기, 항전장치 등 일부 프로그래밍 환경에서는 자동(스택 상) 및 정적 할당(글로벌 또는 로컬) 모두 고정 크기 어레이에 대해서도 이 분석을 수행해야 합니다.

스택에 데이터와 반환 주소/프레임 포인터를 모두 저장하는 아키텍처에서는(내가 아는 바로는 그것) 변수의 주소를 가져올 수 있기 때문에 스택에 할당된 변수는 위험할 수 있으며 체크되지 않은 입력 값은 모든 종류의 장난을 허용할 수 있습니다.

이식성은 임베디드 공간에서는 그다지 중요하지 않지만 신중하게 제어된 환경 이외에서는 alloca()를 사용하지 않는 것이 좋습니다.

임베디드 공간 이외에서는 효율성을 위해 주로 로깅 및 포맷 함수 내부에 alloc()를 사용했습니다.또한 토큰화 및 분류 중에 임시 구조(alloc()를 사용하여 할당됨)가 생성되고 함수가 반환되기 전에 영구 객체(malloc()가 채워집니다.영속적인 오브젝트가 할당되었을 때 작은 임시 구조에 alloca()를 사용하면 플래그멘테이션이 대폭 감소합니다.

왜 아무도 GNU 문서에서 소개한 이 예를 언급하지 않는가?

https://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.html

로컬이 아닌 종료:longjmp('Non-Local Exits' 참조)를 사용하여 할당된 공간을 자동으로 해방합니다.alloca호출한 함수를 통해 종료할 때alloca이것이 사용하는 가장 중요한 이유입니다.

읽기 순서 제안1->2->3->1:

  1. https://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.html
  2. 로컬이 아닌 출구의 도입 및 상세
  3. 할당 예시

이유는 다음과 같습니다.

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

아무도 이 코드를 쓸 수 없다는 게 아니라 당신이 전달하고 있는 크기 인수는alloca거의 확실히 어떤 종류의 입력으로부터 옵니다.그것은 악의적으로 당신의 프로그램을 목표로 할 수 있습니다.alloca그런 거 있잖아요.입력에 근거하지 않는 사이즈나 큰 사이즈의 로컬 버퍼가 없는 경우는, 왜 작은 고정 사이즈의 로컬 버퍼를 선언하지 않았습니까?

사용하는 거의 모든 코드allocaC99 vlas에는 크래시(행운의 경우) 또는 특권 침해(행운의 경우)로 이어지는 심각한 버그가 있습니다.

alloca()는 매우 효율적입니다.하지만 그것은 또한 깊이 부서졌다.

  • 중단 범위 동작(블록 범위 대신 기능 범위)
  • use inconsistant with malloc (alloca()-ted pointer shouldn't be freed, henceforth you have to track where you pointers are coming from to free() only those you got with malloc())
  • bad behavior when you also use inlining (scope sometimes goes to the caller function depending if callee is inlined or not).
  • no stack boundary check
  • undefined behavior in case of failure (does not return NULL like malloc... and what does failure means as it does not check stack boundaries anyway...)
  • not ansi standard

In most cases you can replace it using local variables and majorant size. If it's used for large objects, putting them on the heap is usually a safer idea.

If you really need it C you can use VLA (no vla in C++, too bad). They are much better than alloca() regarding scope behavior and consistency. As I see it VLA are a kind of alloca() made right.

Of course a local structure or array using a majorant of the needed space is still better, and if you don't have such majorant heap allocation using plain malloc() is probably sane. I see no sane use case where you really really need either alloca() or VLA.

Actually, alloca is not guaranteed to use the stack. Indeed, the gcc-2.95 implementation of alloca allocates memory from the heap using malloc itself. Also that implementation is buggy, it may lead to a memory leak and to some unexpected behavior if you call it inside a block with a further use of goto. Not, to say that you should never use it, but some times alloca leads to more overhead than it releaves frome.

I don't think that anybody has mentioned this, but alloca also has some serious security issues not necessarily present with malloc (though these issues also arise with any stack based arrays, dynamic or not). Since the memory is allocated on the stack, buffer overflows/underflows have much more serious consequences than with just malloc.

In particular, the return address for a function is stored on the stack. If this value gets corrupted, your code could be made to go to any executable region of memory. Compilers go to great lengths to make this difficult (in particular by randomizing address layout). However, this is clearly worse than just a stack overflow since the best case is a SEGFAULT if the return value is corrupted, but it could also start executing a random piece of memory or in the worst case some region of memory which compromises your program's security.

Sadly the truly awesome alloca() is missing from the almost awesome tcc. Gcc does have alloca().

  1. It sows the seed of its own destruction. With return as the destructor.

  2. Like malloc() it returns an invalid pointer on fail which will segfault on modern systems with a MMU (and hopefully restart those without).

  3. Unlike auto variables you can specify the size at run time.

It works well with recursion. You can use static variables to achieve something similar to tail recursion and use just a few others pass info to each iteration.

If you push too deep you are assured of a segfault (if you have an MMU).

Note that malloc() offers no more as it returns NULL (which will also segfault if assigned) when the system is out of memory. I.e. all you can do is bail or just try to assign it any way.

To use malloc() I use globals and assign them NULL. If the pointer is not NULL I free it before I use malloc().

You can also use realloc() as general case if want copy any existing data. You need to check pointer before to work out if you are going to copy or concatenate after the realloc().

3.2.5.2 Advantages of alloca

alloca is not worse than a variable-length array (VLA), but it's riskier than allocating on the heap.

On x86 (and most often on ARM), the stack grows downwards, and that brings with it a certain amount of risk: if you accidentally write beyond the block allocated with alloca (due to a buffer overflow for example), then you will overwrite the return address of your function, because that one is located "above" on the stack, i.e. after your allocated block.

_alloca block on the stack

The consequence of this is two-fold:

  1. The program will crash spectacularly and it will be impossible to tell why or where it crashed (stack will most likely unwind to a random address due to the overwritten frame pointer).

  2. It makes buffer overflow many times more dangerous, since a malicious user can craft a special payload which would be put on the stack and can therefore end up executed.

In contrast, if you write beyond a block on the heap you "just" get heap corruption. The program will probably terminate unexpectedly but will unwind the stack properly, thereby reducing the chance of malicious code execution.

A place where alloca() is especially dangerous than malloc() is the kernel - kernel of a typical operating system has a fixed sized stack space hard-coded into one of its header; it is not as flexible as the stack of an application. Making a call to alloca() with an unwarranted size may cause the kernel to crash. Certain compilers warn usage of alloca() (and even VLAs for that matter) under certain options that ought to be turned on while compiling a kernel code - here, it is better to allocate memory in the heap that is not fixed by a hard-coded limit.

Most answers here largely miss the point: there's a reason why using _alloca() is potentially worse than merely storing large objects in the stack.

The main difference between automatic storage and _alloca() is that the latter suffers from an additional (serious) problem: the allocated block is not controlled by the compiler, so there's no way for the compiler to optimize or recycle it.

Compare:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

with:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

The problem with the latter should be obvious.

IMO the biggest risk with alloca and variable length arrays is it can fail in a very dangerous manner if the allocation size is unexpectedly large.

Allocations on the stack typically have no checking in user code.

Modern operating systems will generally put a guard page in place below* to detect stack overflow. When the stack overflows the kernel may either expand the stack or kill the process. Linux expanded this guard region in 2017 to be significantly large than a page, but it's still finite in size.

So as a rule it's best to avoid allocating more than a page on the stack before making use of the previous allocations. With alloca or variable length arrays it's easy to end up allowing an attacker to make arbitrary size allocations on the stack and hence skip over any guard page and access arbitrary memory.

* on most widespread systems today the stack grows downwards.

ReferenceURL : https://stackoverflow.com/questions/1018853/why-is-the-use-of-alloca-not-considered-good-practice

반응형