C :: [열혈강의 C 프로그래밍] p.115 char_add.c
2010/02/23 01:21
tothefelix 님의 블로그에서 아래의 코드를 가져왔습니다.
/* char_add.c */
#include <stdio.h>
int main(void)
{
char a, b;
char result;
printf("-50 이상 +50 이하의 수 둘 입력: ");
scanf("%d %d", &a, &b);
result=a+b;
printf("두 수의 덧셈 결과: %d \n", result);
return 0;
}언뜻 보면 정상적인 코드인 것 같으나, 유심히 보면 자료형이 잘못된 것을 알 수 있습니다. 1바이트의 char 형 변수에 4바이트의 int 형 값을 넣는 오류입니다. 명백히 코드 상의 오류지요. 정말 열혈강의에 저런 코드가 예문으로 나와 있다면 무척 실망입니다. 역시 국내 C 레퍼런스로는 김상형 님의 "혼자 연구하는 C/C++"가 최고의 레퍼런스가 아닌가 싶습니다.
위 코드가 정확히 어떻게 실행되는지를 확인하려면 디버깅을 해서 볼 수도 있고 스택을 그려서도 알 수 있습니다. 이 글에서는 간단하게 스택을 그려보겠습니다.

아마 컴파일러는 잘못된 자료형에 의한 실행 오류를 막기 위해서 스택에 dummy를 임의적으로 만들 겁니다. 그리고 scanf() 함수에 의해 변수 a와 b에 각각 값을 넣겠지요. [그림1]은 각각 12와 49를 입력하였다고 가정하였습니다.
원래 변수 a는 1바이트의 char 형이지만, 값을 넣을 때 4바이트의 int 형인 척 들어갑니다. 그런데 문제는 다음 변수 b에 값이 들어갈 때 입니다. 이 때도 마찬가지로 1바이트의 b에 4바이트인 척 값이 들어가므로 앞의 0x0c 에 0x00 이 덮어씌워집니다. 이렇게 되면 a의 값은 0이 되지요. 그러니 둘을 더하면 b의 값이 나오게 됩니다. 그리고 그 값인 result 역시 원래 1바이트의 char 형임에도 4바이트인 척 들어가지므로, 앞의 0x31 이 0x00 으로 변경됩니다. 그래서 마지막에 printf() 함수로 result를 출력하면, 12593(0x3131)이 아닌 49(0x31)이 되는 것입니다.
정리하면, 1바이트의 char 형 변수에 4바이트의 int 형인 척 값을 대입하다보니, 뒤의 값이 앞의 값을 덮어씌워서 잘못된 결과를 내게 되는 것이죠.
그렇다면 어떻게 해야 정상적인 결과를 얻을 수 있을까요? 간단하게 char 형을 int 형으로 변경해주면 해결됩니다.
그리고 만약 위와 같이 잘못된 자료형에 의한 오류가 존재하고 컴파일러가 [그림1]과 같은 dummy를 생성하지 않는다면, ebp를 사용자가 조작할 수 있는 보안상의 취약점이 됩니다.
"0x08 과거의 글모음 / 내 머리 속의 노트" 분류의 다른 글
| 블로그(텍스트큐브) 서버를 직접 만들어보기 - OS & Package 설치 | 2011/02/16 |
| C/Linux :: 리눅스에서 파일 덤프... dump.c | 2010/12/05 |
| C/Linux :: ls -l 을 수행하는 ll.c | 2010/10/30 |
| python :: Socket Communication with Thread | 2010/10/10 |
| 리눅스(우분투)에서 부경대학교 무선랜 접속하기 | 2010/07/19 |
Trackback Address:http://hisjournal.net/blog/trackback/310
보안상의 문제를 떠나서 왜 저런 기본적인 실수를 했을까 저자의 의도가 궁금하네 그려
값의 크기가 작으면 char형으로도 뭐가 좀 되긴 하지만 이는 일반적이지 않으니 int형으로 해야 한다 뭐 이런걸 역설적으로 설명하고 있습니다. 그니까 책이 잘못된건 아니죠
" 우선 감사드립니다. ^^
" 혼자 연구하는 C/C++ 어디서 본 것 같은데... 했더니
http://www.winapi.co.kr 이곳에 있는 것이었네요 ^^
" 음... 그럼 코드상의 문제인거군요...
그런데 예문의 취지에 맞게 변수를 자료형 char로 선언하고
%d를 %c로 바꾸어 해봤는데 그래도 결과는 에러군요.
result가 문자로 출력됩니다.
" 검색해 보니 %c로 수정 후 result가 문자로 표시되는 이유는
char가 문자형이라 그런가 봅니다.
" 예문을 실어 놓은 취지--char형으로 변수를 선언해 덧셈 연산하기--
에 맞게 예문을 수정하려면 어떻게 수정해야 할까요???
" 문득 a, b 는 char 로 선언하고 %c 를 쓰고
result만 int 로 선언하고 %d 를 쓰면
각각 할당된 메모리만 쓰니 괜찮겠구나... 생각했는데 이것도 에러네요 ㅠㅠ
우선 알아두어야 할 것은 사용자가 숫자 1을 입력하였다고 그게 1인 것은 아니란 것입니다. 입력으로 1을 입력하면 정확하게는 0x31 이라는 아스키 문자값이 입력되지요. 이것을 int 형 수로 변환하는 함수가 존재합니다.
그리고 "문득 a, b 는 char 로 선언하고 %c를 쓰고 result만 int 로 선언하고 %d를 쓰면" 에서도 마지막 result에 %d로 값을 넣으면 위 글과 같이 앞의 값들이 덮어씌워지는 문제가 있습니다. %d는 4바이트를 집어넣으니까요.
여기서 가능한 방법 중의 하나는 char 형은 그대로 두고, 모든 %d를 %c로 변경하는 것입니다. 그렇게 되면 입력된 아스키 값끼리 연산이 되고 출력 때도 아스키 값이 출력되지요. 여기서 입력은 항상 한 자리여야 하구요. 만약 자리수를 늘리고 싶으면 char 형 배열을 만들어야 합니다.
하지만 이런 식의 코딩을 권하고 싶지 않네요. 자료형은 의도하는 연산에 맞게 정확한 자료형으로 선언해주어야 나중에 버그를 예방할 수 있습니다.
친절한 설명 감사합니다 ^^
아마 컴파일러마다 차이가 있을텐데
실제로 메모리 할당이 그림 그려주신것처럼 되지 않을껍니다. (vs 기준으로는 제 설명이 맞을껍니다. gcc의 경우는 음......예전에 테스트해본거 같긴한데 기억이 가물가물...)
!!00@@00$$00
!! : a
@@ : b
## : result
00은 빈공간
실제로는 이것처럼 4byte 단위로 할당이 되고 스택상에서 2byte는 비어지는게 맞을껍니다.
사용할 수 있도록 할당된 공간은 아니지만 예약된 공간으로 봐야겠지요.
그러므로 실제로 2byte 할당이 되었지만 그 2byte를 초과하여도 다른 변수 영역을 침범하는게 아니라, 2byte의 비어진 공간을 덮어씌우게 될껍니다.
아마 초과하지도 않을꺼 같긴하네요.
그러므로 데이터 손실은 발생하지 않을껍니다.
하지만 좋은 코드는 아니라는 말씀은 동의합니다.
예, 말씀처럼 MSVC 에서는 4바이트 단위로 할당이 되어서 데이터 손실은 발생하지 않습니다. gcc 에서는 위 [그림1]에서 dummy가 4바이트 가량 더 크게 할당되구요. 컴파일러마다 다르지요. 좋은 말씀 고맙습니다.
저번에 말하던 그거구만.. MSVC는 저런식으로 안생기지
더미 넣어주는거 같드라 나름 안정성(?)인가?ㅎㅎㅎ 자료손실
없었고 GCC는 자료손실이 생겼던거 같네.ㅋ 머 모르는 사람은
그런갑다 하겠는데 좀 아는 사람한테는 BOF인가?ㅋㅋㅋㅋ
제가 알기로는 일부러 char함수를 쓴걸로 알고 있습니다만... 아닐수도 있고요ㅎ
와 ㅋㅋ 솔직히 너무 속보이네요.
페이지까지 제대로 제시해주셨다면 본문을 보셨을텐데 이런 글을 쓰시다니..
열혈강의에서 따오셨다는 예제 char_add.c 는 명백히 저자가 잘못된 예제로써 제시한 것인데 앞 뒤 다 짤라먹고 "책에 이런 잘못된 예제가 나와있다니 실망입니다."하면서 다른 책에 더 잘 설명되있다고 말하시다니..
열혈강의는 깎아 내리면서 다른 책 홍보하는 걸로 밖에 비치지 않네요.ㅋㅋㅋ
혹여 그런 의도가 아니더라도 이런 류의 포스팅 하실거면 확실히 살피시고 사실여부 확인하신다음에 하는게 바람직하지 않을런지요?
전 열혈강의를 일어본 적이 없습니다. 저 예제에 대한 문의가 있어서 포스팅하였습니다. 제 글을 처음부터 읽어보셨다면 이를 알 수 있을건데요.
"정말 열혈강의에 저런 코드가 예문으로 나와 있다면 무척 실망입니다."라는 문장에서 앞 부분을 싹 자르고 "책에 이런 잘못된 예제가 나와있다니 실망입니다."라고 댓글을 다시니 제 포스팅을 깍아내리는 걸로 밖에 비치지 않네요.
그리고 코드 상에서 어떤 문제가 있는지를 위한 포스팅이기 때문에 사실 여부를 판단할 필요가 없고 그럴 의향도 없습니다. 제대로 글을 읽은 독자라면 오해하지 않을 거라고 판단하기 때문입니다.