Linux, 6년·360개 이상 패치 끝에 strncpy API 제거
요약
Linux 커널 개발팀이 6년과 360개 이상의 패치 과정을 거쳐 보안 취약점과 버그의 원인이 되던 strncpy API를 제거하기로 결정했습니다. 이는 C 언어 표준 라이브러리의 한계를 극복하고 더 안전한 문자열 처리 함수(strscpy 등)로 전환하려는 시도입니다.
핵심 포인트
- strncpy는 NUL 종료 처리 미흡과 불필요한 0 패딩으로 인해 버그의 주요 원인이 됨
- Linux 커널은 strscpy 등 더 안전하고 직관적인 대체 함수를 도입함
- C 표준 라이브러리가 현대적인 언어 기능(stringview 등)을 반영하지 못하는 문제 지적
- 문자열 처리 시 UTF-8 사용과 명확한 구분자 활용의 중요성 강조
예전에는 세계 최고 수준의 C 개발자라는 Linux 커널 개발자들이 stringbuffer나 stringview 타입을 만들 줄 모른다고 놀리곤 했지만, 지금처럼 이 주제에 대한 합의가 있던 시절은 아니었으니 어느 정도는 이해됨
제대로 된 방향을 이미 봤던 사람은 Dennis Ritchie였고, 1990년에 C용 팻 포인터 타입을 제안했음. C99에 들어갔다면 완벽한 추가 기능이었을 텐데, 위원회가 그걸 넣었다면 세상이 꽤 달라졌을 수도 있음
2007년에 Walter Bright의 “C's greatest mistake” 글이 나오면서 두 번째 기회가 있었고, 본질적으로 Ritchie와 같은 아이디어인 슬라이스/stringview를 더 명확하게 설명했지만 C11에도 들어가지 못했음. C23까지 왔는데도 여전히 없고, 대신 _Generic과 VLA는 얻었으니 신나게 파티나 하자는 느낌
VLA는 C11에서 선택 기능으로 강등됐고, 그건 좋은 일이라고 봄
지금 더 큰 문제는 C 표준 라이브러리가 여전히 K&R 시대에 묶여 있고, C99에 추가된 구조체 인자나 반환값 같은 언어 기능조차 표준 라이브러리 API에 반영되지 않았다는 점임. 표준 라이브러리에 포인터/크기 쌍인 범위 구조체와 그걸 쓰는 새 문자열 함수나 갱신된 문자열 함수만 있어도 꽤 나아질 수 있음
팀워크에서 제일 거슬리는 패턴이 이거임. 해법 A, B, C가 있고 각각 장단점이 있어서 2주 동안 토론한 뒤, 결국 아무것도 선택하지 않음
그건 WG14의 우선순위가 어디에 있는지 보여줄 뿐임
Linux 커널 안의 strncpy는 직관에 반하는 의미론, NUL 종료 처리, 목적지에 불필요하게 0을 채우는 성능 문제 때문에 몇 년 동안 “끈질긴 버그 원천”이었다고 함
C 코드 리뷰를 부탁받을 때마다 strncpy를 찾아봤고, 늘 거기서 버그를 발견했음
40년 동안 거슬렸던 것들이 있음. NUL 종료 문자열, 그리고 이제는 입출력에서 UTF-8이 아닌 문자열까지 포함됨
줄 끝을 LF, CR, CRLF로 처리하는 관습도 그렇고, 파이프나 쉼표로 필드를 구분하는 방식도 그렇다. GS, FS, RS 같은 모호하지 않은 ASCII 문자를 썼다면 줄 끝 인코딩/디코딩은 입출력의 문제가 되고, HT/VT/CR/LF/FF는 말 그대로 출력 관련 코드로 남을 수 있었을 것임
ASCII의 필드/레코드 구분 문자로 프레이밍된 데이터를 변환하는 프로젝트를 해봤는데, 정말 쉽게 처리됐음
쉼표 구분 데이터에서 생기는 지저분한 이스케이프 처리 고민이 사라져서 훨씬 단순해짐
Unicode에는 이제 더 많은 선택지도 있음. EBCDIC에서 온 듯한 NL Next line, Unicode가 만든 LS Line separator, PS Paragraph separator가 있음
Unicode 표준은 CR, LF, CRLF와 위 문자들뿐 아니라 세로 탭과 폼 피드도 줄 구분자로 처리해야 한다고 말함
표준 입력/출력에서 UTF-8은 완벽하게 잘 동작함. 물론 국제 텍스트 인코딩에서는 90년대 초반에 머물러 있는 Windows가 아니라면 그렇다는 얘기임
LF, CR, CRLF 같은 줄 끝은 운영체제 관습이기도 하고, 프로그래밍 언어가 올바른 줄 끝을 “추측”하려 들지 않는 편이 더 낫다. 이건 해결하는 것보다 더 많은 문제를 만들고, 다시 말하지만 대체로 Windows 특유의 문제라 Microsoft가 Windows를 현 세기로 데려와야 할 일임
LF가 가장 말이 되지만, 텍스트 파일이라면 어느 쪽이든 괜찮음. 문제는 CSV가 텍스트가 아니라는 데 있음
마지막으로 bash에서 CSV 파일을 다뤄야 했을 때는 내부적으로 RS와 FS로 변환해서 처리했음
그냥 어디서나 UTF-8을 쓰면 된다고 봄
strncpy 대신 Linux 커널 코드에서는 NUL 종료 목적지에는 strscpy(), 0 패딩이 필요한 NUL 종료 목적지에는 strscpy_pad(), NUL 종료가 아닌 고정 폭 필드에는 strtomem_pad(), 명시적 패딩이 있는 경계 복사에는 memcpy_and_pad(), 길이를 아는 메모리 복사에는 memcpy()를 쓰라고 함
이건 악몽 같고, 이렇게까지 복잡해야 하는지 모르겠음
이유는 성능임. 이 대부분을 처리하는 안전한 만능 함수는 내부 분기 때문에 느려질 수밖에 없고, 어떤 함수를 고르느냐에는 개발자의 의도가 담겨 있음
코드를 읽을 때 함수 선택만으로 개발 의도가 명확히 보이는 편이 더 낫다고 봄
strncpy를 제대로 쓰는 건 원래부터 늘 복잡했음
최소한 이름이라도 좀 더 낫게 지을 수는 없었을까 싶음
이런 지루한 반복 작업이야말로 시스템 엔지니어링의 진짜 일이 이루어지는 곳임
Linux 커널을 전체 과정 내내 실사용 가능하게 유지하면서 더 신뢰성 있게 만드는 이런 대형 인프라 프로젝트는 몇 달이 아니라 수십 년 단위로 움직임
왜 수십 년 규모가 되는지는 이해함. 사용자와 의존성의 긴 꼬리가 정말 길기 때문임
하지만 그 속도로 장기적인 의미 있는 진전을 만들 수 있는지는 잘 모르겠음. 불평이라기보다는 핵심 인프라의 역설에 가까움
대단하고 겸손해지는 작업임. 이렇게 많은 사람이 기여했다는 게 놀라움
“멋진 새 기능”은 공로를 인정받기 쉽지만, 커널처럼 근본적인 대상에서는 나쁜 기능을 제거하는 일이 오히려 더 중요할 수도 있음
50년 뒤 사람들이 소스 코드를 읽는 법을 잊고 Claude/Codex 찌꺼기가 조용히 쌓이며 지구 에너지의 대부분을 태우는 시대가 오면, 이런 일들이 “창건 시대”의 전설처럼 남을 것 같음
Vernor Vinge의 Deepness in the Sky가 떠오름. 거기서는 어떤 사람이 소프트웨어 고고학으로 우주선을 유지보수함
Unix epoch이 뭔지 아는 유일한 사람이기도 함
50년 뒤 모두가 소스 코드를 이해하는 법을 잊지는 않을 것 같음. 사물이 어떻게 작동하는지 알고 싶어 하는 인간의 욕구는 그때도 남아 있을 것임
AI가 만든 잡탕 코드는 그보다 훨씬 전에 감당 불가능해질 거라고 봄
0 종료 문자열은 컴퓨팅 역사상 가장 큰 실수라고 생각함. Pascal식 문자열이 훨씬 안전했음
Visual Basic, 그리고 나중의 COM이 택한 BSTR 같은 중간 지점도 있음
여전히 0으로 끝나는 문자 배열을 가리키는 포인터지만, 포인터가 가리키는 첫 바이트 바로 앞에 길이 필드가 있음. 내장 NUL 문자가 없다는 가정하에서는 C 문자열과도 호환되고, BSTR 타입 함수는 길이 값을 활용할 수 있음
어느 정도 동의하지만 크기 필드의 자료형을 두고 다툼이 있었을 것임. 가변 길이가 아니었다면 더 그랬고, 가변 길이라면 또 다른 문제가 있었을 것임
한동안은 16비트조차 너무 과하다고 느꼈을 수 있고, 지금은 32비트가 너무 작아 보일 수 있음. “강한 타입” 언어라는 C는 정작 중요했던 곳에서는 꽤 느슨함
0 종료 문자열은 엄청나게 많은 유용한 소프트웨어의 기반이었음. 컴퓨팅 최대의 실수라고 부르는 건 좀 과장임
Pascal 관련 코드는 30년 넘게 안 짰지만, 당시에도 문자열 시스템이 너무 쓰기 어렵다고 생각했던 희미한 기억이 있음
255자면 모두에게 충분해야 하는 거 아니었나?
줄바꿈 종료 라인만큼이나 나쁨
문자열 자료형 하나 없어서 겪는 고통과 삽질이 너무 많음
정확히는 문자열 자료형이 없어서가 아니라, C에 문자열 자료형이 없다는 사실을 우회하느라 생기는 고통과 삽질임
여기서 강한 자료형을 도입하려면 어떤 방식이 가능할까? strncpy 주변 코드도 그 타입과 함수들을 쓰도록 대규모 리팩터링해야 하지 않을까 싶음
strncpy 사용처를 다시 쓰는 데 무엇이 그렇게 어려워서 6년이나 걸렸는지 궁금함
사용처가 그만큼 넓었는지, 아니면 같은 파일을 건드릴 일이 있을 때만 바꾸는 장기 작업이었는지, 혹은 다른 어려움이 있었는지 알고 싶음
Win32 앱에서 공백 패딩 문자열을 쓰는 코드를 다룬 적이 있음. 목적지 문자열은 공백으로 패딩되지만 마지막 바이트에는 여전히 널 문자가 있었음
길이, 복사 같은 작업에는 전용 문자열 함수 버전을 써야 했음. 왜 그랬는지는 모르겠지만, 코드베이스가 워낙 오래돼서 Pascal 구조체 동작에서 기원했을 수도 있음
SQL 데이터베이스의 char 필드에서 온 문자열이라 그랬을 수도 있음. varchar가 아니라 char 필드는 공백으로 패딩됨
이 동작의 뿌리는 Pascal이 아니라 COBOL일 것 같음
문자열 크기가 바뀔 때 재할당을 막으려던 것일 수도 있고, CPU 캐시 라인 정렬 때문일 수도 있음
AI 자동 생성 콘텐츠
본 콘텐츠는 RSS: GeekNews (한국어)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기