#c
#c
Вопрос:
Итак, я пытался повторно реализовать старую технику из sudo для разделения большого сообщения на две строки, исходный код взят из sudo logging.c:
static void
do_syslog(pri, msg)
int pri;
char *msg;
{
int count;
char *p;
char *tmp;
char save;
/*
* Log the full line, breaking into multiple syslog(3) calls if necessary
*/
for (p = msg, count = 0; count < strlen(msg) / MAXSYSLOGLEN 1; count ) {
if (strlen(p) > MAXSYSLOGLEN) {
/*
* Break up the line into what will fit on one syslog(3) line
* Try to break on a word boundary if possible.
*/
for (tmp = p MAXSYSLOGLEN; tmp > p amp;amp; *tmp != ' '; tmp--)
;
if (tmp <= p)
tmp = p MAXSYSLOGLEN;
/* NULL terminate line, but save the char to restore later */
save = *tmp;
*tmp = '';
if (count == 0)
SYSLOG(pri, "%8.8s : %s", user_name, p);
else
SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p);
*tmp = save; /* restore saved character */
/* Eliminate leading whitespace */
for (p = tmp; *p != ' '; p )
;
} else {
if (count == 0)
SYSLOG(pri, "%8.8s : %s", user_name, p);
else
SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p);
}
}
}
Однако, когда попробуйте тот же код, просто заменив syslog
на printf
, и MAXSYSLOGLEN на 20 вместо 960
#include <stdio.h>
#include <string.h>
#define MAXSYSLOGLEN 20
void remake(char *msg){
int count;
char *p;
char *tmp;
char save;
for (p = msg, count=0; count < strlen(msg)/MAXSYSLOGLEN 1; count ){
if( strlen(p) > MAXSYSLOGLEN) {
printf("[*] The message is greater than MAXSYSLOGLENn");
for (tmp = p MAXSYSLOGLEN; tmp > p amp;amp; *tmp != ' '; tmp-- );
if( tmp <= p )
tmp = p MAXSYSLOGLEN;
save = *tmp;
*tmp = '';
if (count == 0)
printf("%sn",p);
else
printf("(command continued) %sn",p );
*tmp = save;
for (p = tmp; *p != ' '; p );
} else {
if (count == 0)
printf("%sn",p);
else
printf("(command continued) %sn",p );
}
}
}
int main(int argc, char* argv[])
{
remake(argv[1]);
}
Теперь к результату:
gcc test.c -o test
./test "this is a command ....dklckdsmcsklsmdcdd"
[*] The message is greater than MAXSYSLOGLEN
this is a command
[*] The message is greater than MAXSYSLOGLEN
(command continued) ....dklckdsmcsklsmd
(command continued) ERROR;JS LOG
последняя строка странная, откуда вообще взялась эта строка? ERROR;JS LOG
это что-то с памятью?
Заранее спасибо.
Ответ №1:
TLDR: Выбросьте этот ужасный исходный код и напишите свой собственный код из scrath.
Описание
Ошибка возникает из исходного кода — здесь:
/* Eliminate leading whitespace */
for (p = tmp; *p != ' '; p )
;
В случае, если в оставшейся части строки нет пробела, p
оно будет увеличиваться снова и снова и в конечном итоге будет указывать за пределы строки. Как только p
он выходит за пределы строки, и вы разыменовываете его (т. Е. *p
), У вас неопределенное поведение, т. Е. Ваша программа может делать что угодно, она может печатать что угодно, аварийно завершать работу, выключать ваш компьютер… что угодно.
Теперь посмотрите на комментарий:
/* Eliminate leading whitespace */
Хммм… но это не то, что делает код! Оно увеличивается p
до тех пор, пока нет пробела. Таким образом, код скорее «устраняет все, что не является пробелом», и он не останавливается, когда строка заканчивается. Ужасно.
Вероятно, они намеревались:
/* Eliminate leading whitespace */
for (p = tmp; *p == ' '; p )
;
Это исправит многие случаи ошибок.
Но даже это приведет к сбою для некоторого ввода… Попробуйте с:
"aaa aa"
^^^^^^^^
Lots of spaces, e.g. 100 spaces
Пробелы будут устранены, но цикл, т.е.
for (p = msg, count = 0; count < strlen(msg) / MAXSYSLOGLEN 1; count ) {
не отрегулировано, поэтому будет напечатано несколько «непреднамеренных» строк.
В качестве второстепенного: обратите внимание, что в комментарии указано «пробел», но код проверяет только наличие «обычных» пробелов. В коде должна была использоваться isspace
функция.
Единственное, что нужно сделать, это выбросить этот ужасный исходный код и написать свой собственный код из scrath.