LINUX.ORG.RU

Непонятная ошибка распределения памяти

 , ,


0

2

Здравствуйте, уважаемые форумчане.

Столкнулся с очень странной ошибкой в алгоритме декодирования Base64.Во время работы алгоритма магическим образом на 4-й итерации цикла затирается внутренняя переменная unsigned long outmax. При чем эта ошибка проявляется только на 32-ных реализациях. Скомпилировал код под 64 бита - всё нормально. Подозреваю, что как-то HEAP портится, но не могу понять - как. Прошу помощи. Вот полный код программы с алгоритмом:

#include <string.h>
#include <stdarg.h>

#include <ctype.h>
#include <stdlib.h>

static int
xmlBase64Decode(const unsigned char *in, unsigned long *inlen,
	        unsigned char *to, unsigned long *tolen) {
    unsigned long incur;		/* current index in in[] */
    unsigned long inblk;		/* last block index in in[] */
    unsigned long outcur;		/* current index in out[] */
    unsigned long inmax;		/* size of in[] */
    unsigned long outmax;		/* size of out[] */
    unsigned char cur;			/* the current value read from in[] */
    unsigned char intmp[3], outtmp[4];	/* temporary buffers for the convert */
    int nbintmp;			/* number of byte in intmp[] */
    int is_ignore;			/* cur should be ignored */
    int is_end = 0;			/* the end of the base64 was found */
    int retval = 1;
    int i;

    if ((in == NULL) || (inlen == NULL) || (to == NULL) || (tolen == NULL))
	return(-1);

    incur = 0;
    inblk = 0;
    outcur = 0;
    inmax = *inlen;
    outmax = *tolen;
    nbintmp = 0;

    while (1) {
        if (incur >= inmax)
            break;
        cur = in[incur++];
        is_ignore = 0;
        if ((cur >= 'A') && (cur <= 'Z'))
            cur = cur - 'A';
        else if ((cur >= 'a') && (cur <= 'z'))
            cur = cur - 'a' + 26;
        else if ((cur >= '0') && (cur <= '9'))
            cur = cur - '0' + 52;
        else if (cur == '+')
            cur = 62;
        else if (cur == '/')
            cur = 63;
        else if (cur == '.')
            cur = 0;
        else if (cur == '=') /*no op , end of the base64 stream */
            is_end = 1;
        else {
            is_ignore = 1;
	    if (nbintmp == 0)
		inblk = incur;
	}

        if (!is_ignore) {
            int nbouttmp = 3;
            int is_break = 0;

            if (is_end) {
                if (nbintmp == 0)
                    break;
                if ((nbintmp == 1) || (nbintmp == 2))
                    nbouttmp = 1;
                else
                    nbouttmp = 2;
                nbintmp = 3;
                is_break = 1;
            }
            intmp[nbintmp++] = cur;
	    /*
	     * if intmp is full, push the 4byte sequence as a 3 byte
	     * sequence out
	     */
            if (nbintmp == 4) {
                nbintmp = 0;
                outtmp[0] = (intmp[0] << 2) | ((intmp[1] & 0x30) >> 4);
                outtmp[1] =
                    ((intmp[1] & 0x0F) << 4) | ((intmp[2] & 0x3C) >> 2);
                outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F);
		if (outcur + 3 >= outmax) {
		    retval = 2;
		    break;
		}

                for (i = 0; i < nbouttmp; i++)
		    to[outcur++] = outtmp[i];
		inblk = incur;
            }

            if (is_break) {
		retval = 0;
                break;
	    }
        }
    }

    *tolen = outcur;
    *inlen = inblk;
    return (retval);
}


int main (void){
	const unsigned char *in_buf = "ABUACwEAAAYAHQAAAAm0BQMAAAEAAAMA";
	unsigned long in_len = strlen(in_buf);
	unsigned long out_len = (in_len * 6) / 8;
	unsigned char *out_buf = NULL;
	out_buf = malloc(out_len);
	if (out_buf != NULL){
		memset(out_buf, 0, out_len);
		xmlBase64Decode((const unsigned char *)in_buf,(unsigned long *) &in_len, (unsigned char *) out_buf,(unsigned long *) &out_len);
	}
	if (out_len != 0)
    {
    	int i;
    	char *prn_buf = NULL;
    	char temp_buf[3];
    	int prn_buf_len = out_len * 3 + 1;
    	prn_buf = malloc(prn_buf_len);
    	if (prn_buf != NULL){
    		memset(prn_buf, 0, prn_buf_len);
    		for (i = 0; i < out_len; i++){
    			sprintf(&temp_buf[0], " %.2X", out_buf[i]);
    			prn_buf[i * 3] = temp_buf[0];
    			prn_buf[i * 3 + 1] = temp_buf[1];
    			prn_buf[i * 3 + 2] = temp_buf[2];
    		}
    		prn_buf[out_len * 3] = 0;
    		printf("Packet = [%s]", prn_buf);
    		free(prn_buf);
    	}
    }
}

Скорее всего где-то выходишь за пределы массива и в стеке как раз оказывается твоя переменная outmax, возможно тут:

intmp[3], outtmp[4];
Попробуй размер массива поставить к пример 64 для теста, если все заработает, то в этом месте ошибка.

Int64 ★★ ()
Ответ на: комментарий от Int64

Да, точно. в intmp[3] 4 байта последовательно записывается, а не 3. пропустил. спасибо. я уж думал с кучей какие-то проблемы.

leonopulos ()
Ответ на: комментарий от mv

Освой valgrind.

Валгринд в статические/стековые массивы не умеет (это вообще нереально без специальных аннотаций со стороны компилятора). Есть экспериментальный sgcheck, которые делает какие-то эвристики, но, например, залезание в соседний массив он не отловит.

vzzo ★★★ ()
Ответ на: комментарий от vzzo

На твоём примере поменять на маллок - две секунды.

mv ★★★★★ ()
Ответ на: комментарий от anonymous

освой asan, епт

[boris@dev ~]$ cat test.c
#include <string.h>
int main()
{
	int a[1000], b[1000], c[1000];
	memset(a, -1, sizeof(a));
	memset(b, 0, sizeof(b));
	memset(c, -1, sizeof(c));
	int i = 1500;
	return b[i]; // SANITIZE ME PLZ
}
[boris@dev ~]$ clang -o test test.c -fsanitize=address
[boris@dev ~]$ ./test
[boris@dev ~]$ echo $?
255

Но, согласен, asan с его редзонами имеет больше шансов поймать беду на плюсминусодин, чем valgrind. Но как только мы запихиваем массивы в структуру, фича с редзонами не прокатывает :(

vzzo ★★★ ()
Ответ на: комментарий от mv

Но не менять же во всей программе все массивы на маллокнутые?

vzzo ★★★ ()

let me clang-check that for you

$ clang-check test.c 
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "test.c"
No compilation database found in /tmp or any parent directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
/tmp/test.c:83:57: warning: array index 3 is past the end of the array (which contains 3 elements) [-Warray-bounds]
                outtmp[2] = ((intmp[2] & 0x03) << 6) | (intmp[3] & 0x3F);
                                                        ^     ~
/tmp/test.c:17:5: note: array 'intmp' declared here
    unsigned char intmp[3], outtmp[4];  /* temporary buffers for the convert */
    ^
/tmp/test.c:108:23: warning: initializing 'const unsigned char *' with an expression of type 'char [33]' converts between pointers to integer types with different sign [-Wpointer-sign]
        const unsigned char *in_buf = "ABUACwEAAAYAHQAAAAm0BQMAAAEAAAMA";
                             ^        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/test.c:109:32: warning: passing 'const unsigned char *' to parameter of type 'const char *' converts between pointers to integer types with different sign [-Wpointer-sign]
        unsigned long in_len = strlen(in_buf);
                                      ^~~~~~
/usr/include/string.h:394:35: note: passing argument to parameter '__s' here
extern size_t strlen (const char *__s)
                                  ^
3 warnings generated.
jollheef ★★★★★ ()

Спасибо всем за ответы. Действительно, немного с размером ошибся.

leonopulos ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.