저는 현재 nginx 인스턴스 뒤에있는 C 프로그램 (Linux에서 glibc를 사용하여 컴파일 됨)에 약간의 HTTP 처리를 구축하고 있으며이 sscanf
시나리오에서 인수 토큰 화를 안전하게 연기해야한다고 생각했습니다 .
URI에서 쿼리를 추출하는 것이 간단하다는 것을 알게되어 매우 기뻤습니다.
char *path = "/events?a=1&b=2&c=3";
char query[64] = {0};
sscanf(path, "%*[^?]?%64s HTTP", query); // query = "a=1&b=2&c=3"
하지만 얼마나 빨리 i͏̠͚̣̗̲n͓̭̞̹t͈e҉̝̟̘̺r͈e̫st̩̟̠i͏͈͇n͏̠͍g̞͝ :(
int pos = -1;
char arg[32] = {0}, value[32] = {0};
int c = sscanf(query, "%32[^=]=%32[^&]&%n", &arg, &value, &pos);
의 입력을 위해 a=1&b=2
내가 할, arg="a"
, value="1"
, c=2
, pos=4
. 완벽 함 : 이제 sscanf를 다시 실행 path + pos
하여 다음 인수를 얻을 수 있습니다. 내가 왜 여기에 있습니까?
음, 동안 a=1&
동일하게 위의 동작합니다가, a=1
생산 arg="a"
, value="1"
, c=2
, 와pos=-1
. 이걸 어떻게 만드나요?
문서를 샅샅이 뒤져서 읽었습니다.
n Nothing is expected; instead, the number of characters consumed
thus far from the input is stored through the next pointer,
which must be a pointer to int. This is not a conversion and
does not increase the count returned by the function. The as‐
signment can be suppressed with the * assignment-suppression
character, but the effect on the return value is undefined.
Therefore %*n conversions should not be used.
단락의 50 % 이상이 부기 세부 사항을 언급합니다. 내가보고있는 행동은 논의되지 않았습니다.
Google 검색 결과를 돌아 다니며 Wikipedia의 Scanf_format_string 항목 (최고 인기 항목)에 빠르게 도달 했지만, 어 ...
좋아요 ... 아무도 보지 못하는 기능을 사용하여 여기 회전 초에있는 것 같습니다. 그것은 나의 남은 자신감을 불러 일으키지 않습니다.
것으로 보인다 무엇인지 살펴받는 곳 %n
같은 scanf-internal.c에서 구현을 , 나는 관련 논의에 대한 표준 불일치를 코드 (선)의 60 %를 발견, 구성하는 (39.6 % 구현의 사소한이며, 0.4 %는 실제 코드 전체 " done++;
").
그것은 * 이 나타납니다는 그의 glibc의 동작은 내부 값 떠날 것입니다 * done
(내가 사용하여 액세스 %n
정의되지 않았거나 오히려, - - 일부 동작을 변경합니다 그것을하지 않는 한) 그대로를. %n
이런 식으로 사용 하는 것은 예상치 못한 일이고 내가 완전히 "용이 여기 있습니다"영역에있는 것 같습니까? :(
나는 내가 사용할 것 같지 않다 scanf
...
완전성을 위해 여기에 제가보고있는 내용을 요약 한 것이 있습니다.
#include <stdio.h>
void test(const char *str) {
int pos = -1;
char arg[32] = {0}, value[32] = {0};
int c = sscanf(str, "%32[^=]=%32[^&]&%n", (char *)&arg, (char *)&value, &pos);
printf("\"%s\": c=%d arg=\"%s\" value=\"%s\" pos=%d\n", str, c, arg, value, pos);
}
int main() {
test("a=1&b=2"); // "a=1&b=2": c=2 arg="a" value="1" pos=4
test("a=1&"); // "a=1&": c=2 arg="a" value="1" pos=4
test("a=1"); // "a=1": c=2 arg="a" value="1" pos=-1
}
나는 C 표준 pos
이 귀하의 예에서 의 값 이 변하지 않음을 보장한다고 생각합니다 .
C17 7.21.6.2는 다음과 같이 설명합니다 fscanf
.
(4) fscanf 함수는 형식의 각 지시문을 차례로 실행합니다. 모든 지시문이 실행되었거나 지시문이 실패하면 (아래 설명 참조) 함수가 반환됩니다. 실패는 입력 실패 (인코딩 오류 발생 또는 입력 문자를 사용할 수 없음) 또는 일치 실패 (부적절한 입력으로 인한)로 설명됩니다.
[...]
(6) 일반 멀티 바이트 문자 인 지시문은 스트림의 다음 문자를 읽어 실행됩니다. 이러한 문자 중 하나가 지시문을 구성하는 문자와 다른 경우 지시문은 실패하고 다른 문자와 후속 문자는 읽지 않은 상태로 유지됩니다. 마찬가지로 파일 끝, 인코딩 오류 또는 읽기 오류로 인해 문자를 읽을 수없는 경우 지시문이 실패합니다.
(여기서 "멀티 바이트 문자"에는 &
.) 와 같은 일반 1 바이트 문자가 포함됩니다 .
따라서 귀하의 "a=1"
예에서 지시문 %32[^=]
, =
및 %32[^&]
모두 성공하고 이제 문자열의 끝에 도달했습니다. 7.21.6.7 sscanf
에서 "문자열의 끝에 도달하는 것은 fscanf 함수에 대한 파일의 끝을 만나는 것과 같습니다."라고 설명되어 있습니다. 따라서 문자를 읽을 수 없으므로 &
지시문이 실패 sscanf
하고 더 이상 수행하지 않고 반환됩니다. %n
지시어는 실행되지, 아무것도 그래서 그 값을 수정할 수있는 권한을 지닌 일이 결코 pos
. 따라서 이전과 동일한 값, 즉 -1을 가져야합니다.
저는이 사건이 예상치 못한 일이라고 생각하지 않습니다. 이미 기존 규칙에 적용되어 있으므로 누구도 명시 적으로 언급하지 않았습니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다