범위를 벗어난 배열에 액세스하는 것은 얼마나 위험한가?
범위를 벗어난 배열(C)에 액세스하는 것이 얼마나 위험한가?때때로 어레이 외부에서 읽거나(이제는 프로그램의 다른 일부 또는 그 이상에서 사용하는 메모리에 액세스하는 것을 이해함) 또는 어레이 외부의 인덱스에 값을 설정하려고 하는 경우가 발생할 수 있다.그 프로그램은 때때로 고장 나기도 하지만, 때로는 그냥 실행되어, 예상치 못한 결과만 낳기도 한다.
이제 내가 알고 싶은 것은 이것이 얼마나 위험한가 입니다.그것이 내 프로그램을 손상시킨다면, 그것은 나쁘지 않다.반면에 내 프로그램 밖에서 뭔가가 깨진다면, 내가 어떻게든 전혀 상관없는 기억을 찾아냈기 때문에, 내 생각엔, 그건 매우 나쁜 것 같아.나는 '어떤 일이든 일어날 수 있다', '분열이 가장 덜 나쁜 문제가 될 수 있다', '하드디스크가 분홍색으로 변하고 유니콘이 창문 아래에서 노래하고 있을지도 모른다' 등의 많은 글을 읽었는데, 모두 좋지만, 정말 무엇이 위험한가?
내 질문:
- 배열을 벗어난 곳에서 값을 읽으면 프로그램 외에 다른 어떤 것도 손상될 수 있는가?나는 사물을 보는 것만으로 아무것도 변하지 않는다고 상상할 수도 있고, 예를 들어 내가 우연히 도달한 파일의 '마지막으로 열린' 속성이 바뀔 수도 있다고 생각할 수도 있다.
- 값을 설정하여 어레이를 벗어나면 프로그램 외에 다른 어떤 것이 손상될 수 있는가?이 스택 오버플로 질문에서 나는 어떤 메모리 위치에도 접근할 수 있고, 안전 보장이 없다는 것을 알게 되었다.
- 나는 이제 내 작은 프로그램들을 XCode 내에서 운영한다.그것이 내 프로그램 주변의 기억 밖으로는 도달할 수 없는 추가적인 보호를 제공하는가?XCode를 해칠 수 있을까?
- 본질적으로 버기 코드를 안전하게 실행하는 방법에 대한 추천 사항이 있으십니까?
나는 OSX 10.7, Xcode 4.6을 사용한다.
ISO C 표준(언어의 공식 정의)에 관한 한, 그 범위 밖의 배열에 접근하는 것은 "정의되지 않은 동작"을 가진다.이것의 문자 그대로의 의미는 다음과 같다.
이 국제 표준이 요구하는 어떤 요건도 부과하지 않는, 휴대할 수 없거나 잘못된 프로그램 구성 또는 잘못된 데이터 사용 시 동작
이에 대한 비규범적 주석을 확장한다.
정의되지 않은 동작은 예측 불가능한 결과를 가지고 상황을 완전히 무시하는 것에서부터, 환경의 문서화된 방식으로 변환 또는 프로그램을 실행하는 동안 동작하는 것(진단 메시지 발행 여부와 무관), 변환 또는 실행을 종료하는 것(진단 메시지의 발행과 함께)까지 다양하다.e).
그러니까 이론이 그렇다는 거군.현실은?
"최상의" 경우, 현재 실행 중인 프로그램이 소유하고 있거나(프로그램이 잘못 실행될 수 있음) 현재 실행 중인 프로그램이 소유하고 있지 않은 메모리 일부(아마 분할 오류와 같은 것으로 프로그램이 충돌할 수 있음)에 액세스하십시오.또는 프로그램이 소유하고 있는 메모리에 쓰려고 할 수도 있지만 읽기 전용으로 표시되어 있다. 이것은 프로그램이 중단되는 원인이 될 수도 있다.
즉, 프로그램이 동시에 실행되는 프로세스를 상호 간에 보호하려는 운영 체제에서 실행된다고 가정할 수 있다.만약 당신의 코드가 "베어 메탈"에서 실행된다면, 그것이 OS 커널이나 임베디드 시스템의 일부라면, 그러한 보호는 없다; 당신의 잘못된 코드는 그러한 보호를 제공하기로 되어 있었다.이 경우 하드웨어(또는 주변의 사물 또는 사람)에 대한 물리적 손상을 포함하여 손상 가능성이 상당히 더 크다.
보호되는 OS 환경에서도 보호가 항상 100%인 것은 아니다.예를 들어 권한 없는 프로그램이 루트(관리자) 액세스를 얻을 수 있도록 허용하는 운영 체제 버그가 있다.일반적인 사용자 권한이 있더라도 오작동 프로그램은 과도한 리소스(CPU, 메모리, 디스크)를 소비하여 전체 시스템을 다운시킬 수 있다.많은 악성 프로그램(바이러스 등)이 버퍼 오버런을 이용하여 시스템에 대한 무단 액세스 권한을 얻는다.
(한 가지 과거 사례:코어 메모리가 있는 일부 오래된 시스템에서는 반복적으로 하나의 메모리 위치에 긴밀하게 액세스하면 말 그대로 그 메모리 덩어리가 녹을 수 있다고 들었다.CRT 디스플레이를 파괴하고, 드라이브 캐비닛의 고조파 주파수로 디스크 드라이브의 읽기/쓰기 헤드를 이동시켜 테이블 위를 가로질러 걸어 바닥에 떨어지게 하는 등의 다른 가능성도 있다.)
그리고 항상 걱정해야 할 스카이넷이 있다.
요컨대, 만약 당신이 의도적으로 나쁜 일을 하기 위해 프로그램을 쓸 수 있다면, 적어도 이론적으로는 버기 프로그램이 우연히 같은 일을 할 수 있다는 것이다.
실제로, MacOS X 시스템에서 실행되는 당신의 버기 프로그램이 충돌보다 더 심각한 일을 할 가능성은 거의 없다.하지만 버기 코드가 정말 나쁜 일을 하는 것을 완전히 막을 수는 없다.
한계를 체크하지 않으면 보안 구멍 등 보기 흉한 부작용이 발생할 수 있다.못생긴 것 중 하나는 임의 코드 실행이다.일반 예시: 고정 크기 배열이 있는 경우strcpy()
사용자 제공 문자열을 여기에 배치하기 위해 사용자는 버퍼 오버플로 및 기능이 완료되면 CPU가 반환되는 코드 주소 등 다른 메모리 위치를 덮어쓰는 문자열을 제공할 수 있다.
즉, 사용자가 프로그램을 호출할 수 있는 문자열을 전송할 수 있음exec("/bin/sh")
당신의 모든 데이터를 수집하고 당신의 컴퓨터를 봇넷 노드로 바꾸는 것을 포함하여 그가 원하는 모든 것을 당신의 시스템에서 실행할 수 있는 껍데기로 만들 것이다.
이 작업을 수행하는 방법에 대한 자세한 내용은 재미와 이익을 위해 스택 스매싱을 참조하십시오.
쓰는 방법:
나는 '어떤 일이든 일어날 수 있다', '분열이 가장 덜 나쁜 문제가 될 수 있다', '하드디스크가 분홍색으로 변하고 유니콘이 창문 아래에서 노래하고 있을지도 모른다' 등의 글을 많이 읽었는데, 이 모든 것은 좋지만, 정말 무엇이 위험한가?
그렇게 하자: 총을 장전하는 거야.별다른 목적 없이 창밖으로 가리키고 발사한다.무엇이 위험할까?
문제는 당신이 모른다는 것이다.만약 당신의 코드가 당신의 프로그램을 망가뜨리는 무언가를 덮어쓴다면, 당신은 그것이 그것을 정의된 상태로 멈출 것이기 때문에 괜찮다.그러나 그것이 무너지지 않는다면 문제가 발생하기 시작한다.프로그램이 어떤 리소스를 제어하고 있으며 어떤 리소스를 사용할 수 있는가?나는 그런 오버플로로 인해 야기된 적어도 하나의 주요 이슈를 알고 있다.그 이슈는 생산 데이터베이스의 관련 없는 변환표를 망친 겉보기에는 무의미해 보이는 통계 기능에 있었다.그 결과는 그 후에 아주 비싼 청소였다.사실 이 문제가 하드디스크를 포맷했다면 훨씬 더 저렴하고 다루기가 쉬웠을 것이다. 다른 말로 하자면 분홍색 유니콘이 당신의 가장 작은 문제가 될지도 모른다.
당신의 운영체제가 당신을 보호할 것이라는 생각은 낙관적이다.가능하면 범위를 벗어난 글은 피하도록 하라.
일반적으로 오늘날의 운영 체제(어쨌든 인기 있는 운영 체제)는 가상 메모리 관리자를 사용하여 보호된 메모리 영역의 모든 애플리케이션을 실행한다.프로세스에 할당/할당된 영역 밖의 실제 공간에 존재하는 위치를 단순히 읽거나 쓰는 것은 그리 쉬운 일이 아니라는 것이 밝혀졌다.
직접 답변:
읽기는 다른 프로세스를 직접적으로 손상시키지 않지만 프로그램/프로세스를 암호화, 해독 또는 검증하는 데 사용되는 KEY 값을 우연히 읽게 되면 프로세스를 간접적으로 손상시킬 수 있다.읽고 있는 데이터에 기초하여 결정을 내리는 경우, 범위를 벗어난 판독은 코드에 다소 부정적인/뜻밖의 영향을 미칠 수 있다.
메모리 주소로 액세스할 수 있는 로액션에 기록함으로써 실제로 무언가를 손상시킬 수 있는 유일한 방법은 당신이 쓰고 있는 메모리 주소가 실제로 RAM 위치가 아닌 하드웨어 레지스터(실제로 데이터 저장소가 아닌 일부 하드웨어를 제어하는 위치)인 경우 이다.사실, 다시 쓸 수 없는 (또는 그런 성격의) 한 번 프로그램 가능한 위치를 쓰지 않는 한, 당신은 여전히 일반적으로 어떤 것을 손상시키지 않을 것이다.
일반적으로 디버거 내에서 실행되는 코드는 디버그 모드로 실행된다.디버그 모드에서 실행하면 실행되지 않거나 완전히 불법이라고 간주되는 작업을 수행했을 때 코드가 더 빨리 중지되는 경향이 있다(그러나 항상은 아님).
매크로를 사용하지 마십시오. 어레이 인덱스 범위 검사가 이미 내장된 데이터 구조를 사용하십시오. 등...
추가적으로 위의 정보는 메모리 보호 창이 있는 운영 체제를 사용하는 시스템에만 해당된다는 점을 추가해야 한다.메모리 보호 창(또는 가상 주소 창)이 없는 운영 체제(실시간 또는 기타)를 활용하는 시스템이나 임베디드 시스템의 코드를 작성하는 경우, 메모리에 읽고 쓸 때 훨씬 더 주의를 기울여야 한다.또한 이러한 경우 보안 문제를 방지하기 위해 항상 SAFE 및 SECURE 코딩 관행을 채택해야 한다.
NSArray
목표-C에 특정 메모리 블록이 할당된다.어레이의 범위를 초과하면 어레이에 할당되지 않은 메모리에 액세스할 수 있음을 의미한다.이는 다음을 의미한다.
- 이 기억은 어떤 가치도 가질 수 있다.데이터 유형에 따라 데이터가 유효한지 알 방법이 없다.
- 이 메모리는 개인 키 또는 다른 사용자 자격 증명과 같은 중요한 정보를 포함할 수 있다.
- 메모리 주소가 잘못되거나 보호될 수 있다.
- 메모리는 다른 프로그램이나 스레드에 의해 액세스되기 때문에 값이 변할 수 있다.
- 다른 것들은 메모리 매핑된 포트와 같이 메모리 주소 공간을 사용한다.
- 알 수 없는 메모리 주소에 데이터를 쓰는 것은 당신의 프로그램을 망가뜨리고 OS 메모리 공간을 덮어쓰며 일반적으로 태양을 붕괴시킬 수 있다.
프로그램 측면에서 코드가 배열 범위를 초과하는 경우 항상 알고자 하는 경우.이로 인해 알 수 없는 값이 반환되어 응용 프로그램이 중단되거나 잘못된 데이터가 제공될 수 있다.
프로그램을 루트 또는 다른 권한 있는 사용자로 실행하지 않는 것은 시스템에 해를 끼치지 않으므로 일반적으로 이것은 좋은 생각이다.
임의의 메모리 위치에 데이터를 기록함으로써 각 프로세스가 자체 메모리 공간에서 실행될 때 컴퓨터에서 실행 중인 다른 프로그램을 직접 "손상"하지 않을 수 있다.
프로세스에 할당되지 않은 메모리에 액세스하려고 하면 운영 체제에서 프로그램이 분할 장애를 가지고 실행되지 않도록 중지할 것이다.
따라서 직접(루트로 실행되고 /dev/mem과 같은 파일에 직접 액세스하지 않고) 프로그램이 운영 체제에서 실행되는 다른 프로그램을 방해할 위험은 없다.
그럼에도 불구하고 - 그리고 아마도 이것은 여러분이 위험의 측면에서 들은 이야기일 것이다 - 우연히 임의의 메모리 위치에 무작위 데이터를 무작위로 적음으로써 여러분이 손상시킬 수 있는 모든 것을 손상시킬 수 있다.
예를 들어 프로그램에서 프로그램 어딘가에 저장된 파일 이름으로 지정된 특정 파일을 삭제하려고 할 수 있다.실수로 파일 이름이 저장된 위치만 덮어쓰면 매우 다른 파일을 삭제할 수 있다.
만약 당신이 시스템 레벨 프로그래밍이나 임베디드 시스템 프로그래밍을 한다면, 만약 당신이 임의의 메모리 위치에 글을 쓴다면 매우 나쁜 일들이 일어날 수 있다.오래된 시스템과 많은 마이크로 컨트롤러는 메모리 매핑된 IO를 사용하기 때문에 주변 레지스터에 매핑되는 메모리 위치에 쓰는 것은 특히 비동기적으로 수행될 경우 큰 혼란을 가져올 수 있다.
플래시 메모리를 프로그래밍하는 것이 그 예다.메모리 칩의 프로그래밍 모드는 칩의 주소 범위 내의 특정 위치에 특정한 값의 순서를 기록함으로써 활성화된다.만약 또 다른 프로세스가 진행되는 동안 칩의 다른 위치에 기록된다면, 프로그래밍 사이클이 실패할 것이다.
어떤 경우에는 하드웨어가 주소를 둘러싸게 되므로(대부분 중요한 비트/바이트의 주소는 무시됨) 물리적 주소 공간의 끝을 넘어 주소로 쓰는 것은 실제로 사물의 중간에 데이터를 기록하게 된다.
그리고 마지막으로 MC68000과 같은 구형 CPU는 하드웨어 재설정만이 다시 작동할 수 있을 정도로 잠길 수 있다.몇 십 년 동안 작업을 하지 않았지만 예외 사항을 처리하려고 노력하다가 버스 오류(존재하지 않는 메모리)에 부딪혔을 때 하드웨어 재설정이 주장될 때까지 중단될 것이라고 나는 믿는다.
나의 가장 큰 권고는 제품에 대한 노골적인 플러그지만, 나는 그것에 개인적인 관심도 없고 어떤 식으로든 그것들과 관련이 없다 - 하지만 신뢰성이 중요한 몇 십 년 동안의 C 프로그래밍과 임베디드 시스템을 바탕으로, 김펠의 PC 린트는 그러한 종류의 오류를 감지할 뿐만 아니라, 더 나은 C/C++ 프로그래머를 만들 것이다.나쁜 습관에 대해 끊임없이 입버릇을 늘어놓음으로써 너를 괴롭히는 것이다.
누군가로부터 사본을 얻을 수 있다면 MISRA C 부호화 표준을 읽는 것도 추천한다.나는 최근에 본 적이 없지만, 옛날에는 왜 그들이 커버하는 것들을 해야 하는지/하면 안 되는지에 대해 잘 설명해줬어.
너에 대해서는 모르겠지만, 내가 2차나 3차쯤 어떤 어플리케이션에서 coredump나 전화를 끊었을 때, 그것을 생산한 어떤 회사에 대한 나의 의견은 절반으로 떨어진다.네 번째나 다섯 번째, 그리고 그 소포가 무엇이든 선반용품이 되고, 나는 그것이 다시는 나를 괴롭히지 않도록 하기 위해 그 소포의 중앙을 통해 나무 말뚝을 박았다.
코드를 테스트할 때 Valgrind에서 도구를 사용해 보십시오. 스택 프레임 내에서 개별 어레이 경계 위반을 포착하지는 않겠지만 단일 기능의 범위 밖에서 미묘하고 광범위한 문제를 일으킬 수 있는 문제를 포함하여 다른 종류의 메모리 문제를 많이 포착해야 합니다.
매뉴얼에서:
Memcheck는 메모리 오류 검출기다.C와 C++ 프로그램에서 공통적으로 나타나는 다음과 같은 문제를 감지할 수 있다.
- 힙 블록 오버런 및 언더런닝, 스택 상단 오버런 및 메모리 액세스 등과 같이 해제된 후 해서는 안 되는 메모리에 액세스.
- 정의되지 않은 값, 즉 초기화되지 않았거나 다른 정의되지 않은 값에서 파생된 값 사용.
- 힙 블록 이중 자유화 등 힙 메모리가 잘못되거나 malloc/new/new[]와 free/delete/delete[]의 불일치 사용
- memcpy 및 관련 기능에서 겹치는 src 및 dst 포인터.
- 기억이 새다.
ETA: 하지만, 카즈의 대답에 따르면, 그것은 만병통치약은 아니며, 특히 흥미진진한 액세스 패턴을 사용할 때, 항상 가장 도움이 되는 결과를 주는 것은 아니다.
당신 자신의 프로그램 외에, 나는 당신이 어떤 것도 깨뜨리지 않을 것이라고 생각하는데, 최악의 경우 당신은 커널이 당신의 프로슈즈에 할당하지 않은 페이지에 해당하는 메모리 주소를 읽거나 쓰려고 할 것이고, 적절한 예외를 발생시키고 죽임을 당할 것이다(즉, 당신의 과정이다).
나는 DSP 칩을 위한 컴파일러와 함께 일하고 있는데, 이 컴파일러는 의도적으로 C 코드 중 어레이의 끝을 지나 하나에 접속하는 코드를 생성하지만, 그렇지 않다!
이는 반복의 끝이 다음 반복을 위해 일부 데이터를 프리페치하도록 루프가 구조화되기 때문이다.따라서 마지막 반복이 끝날 때 프리페치된 기준점은 실제로 사용되지 않는다.
그렇게 C 코드를 쓰는 것은 정의되지 않은 행동을 유발하지만, 그것은 그 자체로 최대의 휴대성을 필요로 하는 표준 문서의 형식에 불과하다.
그렇지 않은 경우가 더 많다. 경계 밖으로 접근하는 프로그램은 교묘하게 최적화되지 않는다.그것은 그야말로 마차다.코드는 가비지 값을 가져오고, 앞서 언급한 컴파일러의 최적화된 루프와는 달리, 코드는 이후 계산에서 그 값을 사용하게 되며, 따라서 im을 손상시킨다.
그와 같은 벌레를 잡을 가치가 있고, 따라서 런타임으로 "main.c의 42줄에서 어레이 오버런"과 같은 진단 메시지를 만들 수 있도록 단지 그 이유만으로 행동을 규정하지 않는 것이 가치가 있다.
가상 메모리가 있는 시스템에서는 다음에 나오는 주소가 가상 메모리의 매핑되지 않은 영역에 있도록 어레이가 할당될 수 있다.그러면 접속이 프로그램을 폭파시킬 것이다.
또한 C에서는 배열 끝의 1에 해당하는 포인터를 만들 수 있다는 점에 유의하십시오.그리고 이 포인터는 배열의 내부에 대한 어떤 포인터보다 더 큰 것을 비교해야 한다.이것은 C 구현이 메모리 끝에 어레이를 배치할 수 없다는 것을 의미한다. 메모리 끝에는 1+ 주소가 둘러 싸여 있고 어레이의 다른 주소보다 작아 보인다.
그럼에도 불구하고, 최대 이동성은 아니더라도, 초기화되지 않았거나 범위를 벗어난 값에 대한 액세스는 때때로 유효한 최적화 기법이다.예를 들어 Valgrind 도구는 이러한 액세스가 발생할 때 초기화되지 않은 데이터에 대한 액세스를 보고하지 않지만 나중에 프로그램 결과에 영향을 미칠 수 있는 어떤 방식으로 값이 사용될 때만 이러한 이유로 간주된다."xxx:nnn의 조건부 분기는 초기화되지 않은 값에 따라 달라진다"와 같은 진단을 받게 되는데, 이 분기가 어디서 발생하는지 추적하기가 어려울 수 있다.만약 그러한 모든 액세스가 즉시 제한된다면, 컴파일러 최적화 코드뿐만 아니라 정확하게 손으로 최적화된 코드에서 많은 잘못된 긍정이 발생할 것이다.
말이 나와서 말인데, 나는 이런 오류를 발생시키고 있는 벤더의 코덱과 함께 일하고 있었는데, 리눅스로 포팅되어 발그라인드로 실행되었다.그러나 벤더는 실제로 사용되는 값의 몇 비트만이 초기화되지 않은 메모리에서 나온 것이라고 설득했고, 그 비트들은 논리에 의해 조심스럽게 피했다.값에서 좋은 부분만 사용되었고 Valgrind는 개별 비트로 추적할 수 있는 능력이 없다.초기화되지 않은 자료는 암호화된 데이터의 비트 스트림 끝을 지나 단어를 읽음에서 나왔지만, 코드는 스트림 안에 몇 개의 비트가 있는지 알고 있으며 실제 존재하는 것보다 더 많은 비트를 사용하지 않을 것이다.비트 스트림 어레이의 끝을 넘어선 액세스는 DSP 아키텍처에 해를 끼치지 않기 때문에(배열 후 가상 메모리가 없고, 메모리 맵 포트가 없으며, 주소가 래핑되지 않음) 유효한 최적화 기법이다.
ISO C에 따르면 단순히 C 표준에 정의되지 않은 헤더를 포함하거나 프로그램 자체나 C 표준에 정의되지 않은 함수를 호출하는 것은 정의되지 않은 동작의 예이기 때문에 "정의되지 않은 동작"은 실제로 큰 의미가 없다.정의되지 않은 행동은 단지 "ISO C 표준에 의해 정의되지 않은" 것을 의미하지는 않는다.하지만 물론, 때때로 정의되지 않은 행동은 정말로 누구에 의해 정의되지 않는다.
2개 이상의 차원을 가진 배열은 다른 답변에서 언급된 것 이상의 고려사항을 제시한다.다음 기능을 고려하십시오.
char arr1[2][8];
char arr2[4];
int test1(int n)
{
arr1[1][0] = 1;
for (int i=0; i<n; i++) arr1[0][i] = arr2[i];
return arr1[1][0];
}
int test2(int ofs, int n)
{
arr1[1][0] = 1;
for (int i=0; i<n; i++) *(arr1[0]+i) = arr2[i];
return arr1[1][0];
}
gcc가 첫 번째 함수를 처리하는 방식은 arr[0][i]를 쓰려는 시도가 arr[1][0]의 값에 영향을 미칠 가능성을 허용하지 않으며 생성된 코드는 하드코딩된 값 1 이외의 값을 반환할 수 없다.비록 이 기준서는 다음의 의미를 정의하고 있지만array[index]
와 정확히 동등한.(*((array)+(index)))
, gcc는 배열 유형의 값에 [] 연산자를 사용하는 경우와 명시적 포인터 산술 값을 사용하는 경우를 비교하여 배열 한계와 포인터 붕괴의 개념을 다르게 해석하는 것 같다.
참조URL: https://stackoverflow.com/questions/15646973/how-dangerous-is-it-to-access-an-array-out-of-bounds
'IT이야기' 카테고리의 다른 글
0.1 float가 0.1 double보다 크다.나는 그것이 거짓이라고 기대했다. (0) | 2022.04.27 |
---|---|
Vue 앱에서 자동 복구된 사용자의 데이터를 가져오는 최적의 장소? (0) | 2022.04.27 |
Vuejs는 url에서 이미지를 캐슁하여 다시 fetching하지 마십시오. (0) | 2022.04.27 |
VueJS + Vue.끌 수 있는 + Vuex 스토어 + 계산된 변수 (0) | 2022.04.27 |
__ob__: Observer를 내 배열 목록에서 제거하려면 어떻게 해야 하는가? (0) | 2022.04.27 |