LINUX.ORG.RU

double free or corruption (!prev)


0

0

При запуске приложения со следующей функцией

int stat_image(unsigned char *image, int x0, int y0, int frame_size){
	int i,j, w=Grab_Width;
	int xm = x0 + frame_size;
	int ym = y0 + frame_size;
	double d, avr, std;
	unsigned char *bp, pix, vmin, vmax;
	unsigned int *hist;
	unsigned char notv = 0x23;
	vmax = 0;
	for(j=y0; j<ym; j++){
		bp = &image[w * j + x0];
		for(i=x0; i<xm; i++, bp++)
		    if(vmax < *bp) vmax = *bp;
	}
	if(vmax == 0) return 0;
	hist = (unsigned int*) calloc(vmax, sizeof(int));
	vmin = vmax;
	for(j=y0; j<ym; j++){
		bp = &image[w * j + x0];
		for(i=x0; i<xm; i++, bp++){
			pix = *bp++;
			hist[pix]++;
			if(vmin>pix) vmin = pix;			
		}
	}
	if(vmax==vmin && vmax==notv){ 
		free(hist);
		return 0;
	}
	for (avr=0.,i=0,j=vmin; j<=vmax; j++) {
		avr += (double)hist[j]*j; // накапливаем уровень сигнала в выбранном диапазоне яркостей
		i += hist[j];		// и количество пикселей в заданном диапазоне
	}
	if(avr > 1. && i != 0) avr /= i; // средний уровень яркости
	for(std=0.,j=vmin; j<=vmax; j++){
		d = j-avr;		// отклонение от среднего
		std += d*d*hist[j];	// взвешенный квадрат отклонения
	}
	if(i != 0) std = sqrt(std/i);		// среднеквадратическое отклонение
MSG("\n\nFREE HIST: size=%d, hist[last]=%d\n", vmax, hist[vmax-1]);
	free(hist);
	if(avr < MIN_AVR_LEVEL || std < MIN_STD_LEVEL) return 0;
	else return 1;
}
При попытке сделать free после MSG(...) выскакивает ошибка:

... double free or corruption (!prev) ...

Если я убираю этот free, все работает - но ведь мусор-то тогда накапливается! Судя по MSG, free явно вызывается в первый раз, но почему-то приводит к такой странной ошибке. С чем это может быть связано?

☆☆☆☆☆

> С чем это может быть связано?

Что именно в слове «corruption» непонятно?

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

pix > vmax? hint:

pix не может быть больше vmax (да и падает программа все время на free, а иначе ошибка была бы, наверное, другая - corrupted double-linked list, например).

С указателями тоже понятно: *bp++ означает как раз *(bp++).

tailgunner

Что именно в слове «corruption» непонятно?

Непонятно, чего там повреждено-то могло быть?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Непонятно, чего там повреждено-то могло быть?

Ыыыы... я так подозреваю, что ты вышел за границу массива hist. Убери запись в него и прогони программу - если ошибка исчезнет, ты портишь память.

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

Убери запись в него и прогони программу - если ошибка исчезнет, ты портишь память.

Точно, нашел ошибку. Совсем уже голова не варит: в цикле

      for(i=x0; i<xm; i++, bp++){ 
         pix = *bp++; 
         hist[pix]++; 
         if(vmin>pix) vmin = pix;          
      } 
Дважды делается bp++. Изменил строчку pix=*bp, и ошибки не стало.

Если бы ошибки с alloc'ами были более понятными (например, написали бы: segfault), было бы проще найти ошибку.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Если бы ошибки с alloc'ами были более понятными (например, написали бы: segfault), было бы проще найти ошибку.

Тебе уже много раз сказали - пользуйся языками высокого уровня, там таких ошибок не бывает. Тебе, кстати, еще повезло - удачно память испортил, а часто это проходит незамеченным (а valgrind ты явно не используешь).

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

Не мешай человеку писать программу. Пристал тут со своими валгриндами и прочими фундаментальными проблемами очередей.

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

:)

Валгриндами действительно не пользуюсь. А С по мне - язык достаточно высокого уровня (не ассемблер, все-таки).

Сейчас вот с очередной проблемой столкнулся: как mmap'ить один и тот же набор буферов v4l2, чтобы можно было обрабатывать видео одновременно в двух неродственных процессах (не хочется вручную выделять дополнительные буферы и копировать туда каждый кадр, когда v4l2 и так по идее предоставляет такую возможность).

Но, эту тему я, наверное, подниму завтра - если сам не разберусь.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Сейчас вот с очередной проблемой столкнулся: как mmap'ить один и тот же набор буферов v4l2, чтобы можно было обрабатывать видео одновременно в двух неродственных процессах (не хочется вручную выделять дополнительные буферы и копировать туда каждый кадр, когда v4l2 и так по идее предоставляет такую возможность).

Делаешь mmap() с MAP_SHARED, драйверу буфера через V4L2_MEMORY_USERPTR задаёшь из этого сегмента.

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

А С по мне - язык достаточно высокого уровня

Это ты Коммон Лисп с Хаскеллями не видел.

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

драйверу буфера через V4L2_MEMORY_USERPTR задаёшь из этого сегмента.

Хотелось использовать MMAP, а не USERPTR (т.к. через буферы пользователя тормознутее работает). Ну, этим буду заниматься завтра (в домашнем компьютере TV-тюнера нет).

Это ты Коммон Лисп с Хаскеллями не видел.

Да мне чего только не предлагали изучать, вплоть до IDL :) Но, думаю, лучше не распыляться. Так я только C и JavaScript не знаю (а об остальных понятия не имею), а то еще несколько языков буду не знать и доставать всех расспросами :)

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Вообще, хочу посоветовать: пишите код так, как будто тот, кто его будет читать - это убийца-маньяк и он знает, где Вы живёте. То есть, избегайте конструкций вида x++ или x= (exp) ?y:z;
Лучше напишите
x += 1
или
if (exp)
{
x=y;
}
else
{
x=z;
}
здесь, жертвуя красотой/лаконичностью кода, Вы уважаете время того, кто потом просматривает код. Всё равно толковый оптимизатор в конечном итоге сделает одинаковый код в обеих случаях; однако при этом время, затраченное человеком на осознание кода, на несколько порядков ценнее времени, затраченного компилятором на преобразование кода в машинные команды. «Человек ценнее компьютера» - это первейшая аксиома (которая, кстати, частенько помогает решить множество логических ошибок в коде из-за чрезмерного увлечения лаконичностью и краткостью).

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

пишите код так, как будто тот, кто его будет читать - это убийца-маньяк и он знает, где Вы живёте.

Здесь уже, как говорится, на вкус и на цвет... Во многих учебниках, которые я читал, авторы наоборот советуют пользоваться конструкциями с префиксными/постфиксными инкрементами/декрементами, условными операциями вида (a)?b:c и т.п. сокращениями. Другие, как вы, советуют идти в ущерб лаконичности, но в пользу удобочитаемости. Я, честно говоря, не люблю, когда в одном файле больше трех-четырех сотен строк, поэтому стараюсь по возможности сокращать код (хотя это, с другой стороны, и неправильно) и распределять его по файлам (например, этот «проект» состоит из ~12 сишных и десятка заголовочных файлов).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Slavaz

> избегайте конструкций вида x++ или x= (exp) ?y:z;

what's wrong with 'x= (exp) ?y:z;'?

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

Вы уважаете время того, кто потом просматривает код.

Скорее наоборот - не уважаете. Меня лично бесит стиль вида

if (exp)
{
   x=y;
}
else
{
   x=z;
}

такое впечатление, что у его автора был приступ словесного поноса^W^Wграфомании - 8 строк вместо одной (и да, время на чтение зависит и от количества строк).

Впрочем, это оффтопик и никак не делает Си языком высого уровня.

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

> Во многих учебниках, которые я читал, авторы наоборот советуют пользоваться конструкциями с префиксными/постфиксными инкрементами/декрементами, условными операциями вида (a)?b:c и т.п. сокращениями.
Это теоретики/академики

Другие, как вы, советуют идти в ущерб лаконичности, но в пользу удобочитаемости.

Это практики/внедренцы.

Я, честно говоря, не люблю, когда в одном файле больше трех-четырех сотен строк, поэтому стараюсь по возможности сокращать код

Ну три-четыре СОТН строк - это маловато. Нома под тысячу-полторы, но при этом желательно, чтобы функции в файле были не больше 60-120 строк (два-три экрана по PageUp/PageDown)

(хотя это, с другой стороны, и неправильно)

Почему неправильно? Пока проект из разряда «наклёпано на коленке для сиюминутной задачи», то и решение в одном мегабайтном исходнике сгодится. Но как только этот проектик становится достоянием общественности, то лучше, конечно, потратить немного времени на разбивку этого мегового файла по логическим блокам(функциям по назначению; по классам если ООП и т.п.) для упрощения понимания остальными людьми. И даже если при этом файлы будут по полкило - всё равно, это будет логически разделено и по человечески оправдано. Компилятор всё простит ;)


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

> Скорее наоборот - не уважаете. такое впечатление, что у его автора был приступ словесного поноса^W^Wграфомании - 8 строк вместо одной (и да, время на чтение зависит и от количества строк).

Нет-нет, не поймите как оскорбение Вас лично или всех тех людей, которые подобные конструкции склонны воспринимать как недостаток опыта. это не так. Тут палка о двух концах: либо человек, написавший подобное, действительно начинающий чайник; либо написавший подобное осознаёт, что его код будут читать тысячи людей и не все из них смогут сходу прочитать мудрённые конуструкции (а унарные и тринарные операции - это частный случай мудрённых конструкций). То есть, воспринимайте такое упрощение кода не проявление неувашения (хотя профи может вполне обоснованно воспринять подобное как неуважение к нему лично) - воспринимайте это как попытку помочь воспринять собственный код как можно большим числом людей в как можно сжатые сроки. Понимаю, что всем не угодить, однако и угождать только лишь меньшей части программеров-прыфессионалов тоже не стоит (не все будущие потенциальные патчеры являются профессиональными программистами).

Как-то так, в угоду большинству во имя жизни проекта :)

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

> Нет-нет, не поймите как оскорбение Вас лично или всех тех людей, которые подобные конструкции склонны воспринимать как недостаток опыта

Ну это ты назвал меня теоретиком и академиком %) Стиль, за который ты ратуешь - это пример «обжегся на молоке - дуешь на воду». Никакой ясности он не способствует.

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

> Ну это ты
Ок, с Вами мы не пересекались в негативном плане, посему принимаю приближение на «ты» :) Далее на «ты», если не будет возражений...

Ну это ты назвал меня теоретиком и академиком %)

Вообще-то, не я называл :). Вероятно, это ты себя причислил; а на меня взвалил груз ответственности за классификацию ;). Просто моя классификация совпала с твоим мировозрением. Что не означает, что я утверждал, что ты теоретик и академик :) Впрочем, это уже демагогия (ненавижу жонглирование словесями).

По поводу удобочитаемости: для тебя, вероятно, подобный код и будет снижением удобочитаемости, но для десятка человек это будет восприятием кода на их уровне. Тут остаётся только уповать на то, что ты лично не отвернёшься от подобного проекта(стиль кода не отвратит тебя) и таки опубликуешь назревший собственный патч безопасности, при этом пять «хомячков» опубликуют патчи для добавления свистоперделок.

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

Хотелось использовать MMAP, а не USERPTR (т.к. через буферы пользователя тормознутее работает). Ну, этим буду заниматься завтра (в домашнем компьютере TV-тюнера нет).

Отличие только в том, кто выделяет буфер: драйвер или юзер.

Да мне чего только не предлагали изучать, вплоть до IDL :) Но, думаю, лучше не распыляться. Так я только C и JavaScript не знаю (а об остальных понятия не имею), а то еще несколько языков буду не знать и доставать всех расспросами :)

Учиться надо всю жизнь.

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

Учиться надо всю жизнь.

Это точно. Но я считаю более полезным проштудировать лишний раз мат. анализ, оптику и астрофизику (ну, или «Все про ТеХ» Кнута оживить в воспоминаниях), чем новый ЯП изучать...

Eddy_Em ☆☆☆☆☆
() автор топика

По поводу MMAP V4L2

Не хочу создавать отдельную тему, но приведу вкратце решение.

С MMAP и USERPTR я разобрался. MMAP'ом «поделиться» видеопамятью, к сожалению, нельзя. Зато можно при помощи флага V4L2_MEMORY_USERPTR заставить v4l2 выделять память в буферы пользователя.

Сначала я пытался сделать mmap на /dev/null с разными смещениями. Естественно, ничего не получилось. Тогда я решил сделать mmap через виртуальные файлы из shm_open:

	tkey = ftok(grab_dev, 666);
	shmid = shmget(tkey, sizeof(struct Buffs), IPC_CREAT| IPC_EXCL | 0666);
	if(shmid == -1){
		if( (shmid = shmget(tkey, sizeof(struct Buffs), 0666)) == -1)
			errno_exit("Can't access shared memory");
	}
	else
		shm_new = 1;
	Buffers = (struct Buffs*) shmat(shmid, NULL, 0);
	if(Buffers == (void *) -1) errno_exit("Can't attach shared memory");
	if(shm_new) Buffers->nbuf_cur = -1;
	for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
		buffers[n_buffers].length = buffer_size;
		snprintf(shmname, 31, "/shm%d", n_buffers);
		fd = shm_open(shmname, O_RDWR|O_CREAT, 00666);
		if(fd < 0){shm_unlink(shmname); errno_exit("Can't open shared object");}
		ftruncate(fd, buffer_size);
		buffers[n_buffers].start = mmap(NULL, buffer_size, PROT_READ|PROT_WRITE, 
			MAP_SHARED, fd, 0);
		if (buffers[n_buffers].start == MAP_FAILED)
			errno_exit("Out of memory");
	}
Здесь размещаемая в разделяемой памяти структура Buffers содержит номер текущего буфера и его длину.

В читающем процессе буферы инициализируются так:

	for (i = 0; i < 4; ++i){
		snprintf(shmname, 31, "/shm%d", n_buffers);
		if((fd = shm_open(shmname, O_RDWR, 00666))<0){perror("SHM_OPEN");exit(1);};
		images[i] = mmap(NULL, Buffers->length, PROT_READ|PROT_WRITE, 
			MAP_SHARED, fd, 0);
		if (!images[i]){
			fprintf (stderr, "Out of memory\n");
			exit (EXIT_FAILURE);
		}
Ну а дальше читающий процесс спокойно обрабатывает видео для выдачи клиенту, не мешая основному управляющему процессу. Кроме того, польза от такого разделения еще и в том, что одновременно одно и то же могут просматривать несколько клиентов.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: По поводу MMAP V4L2 от Eddy_Em

С MMAP и USERPTR я разобрался. MMAP'ом «поделиться» видеопамятью, к сожалению, нельзя.

Какая такая видеопамять? V4L(2) принимает данные от устройства (самостоятельно, либо просит об этом DMA-контроллер устройства) и кладёт их в буферы, которые лежат в обычной памяти.

Зато можно при помощи флага V4L2_MEMORY_USERPTR заставить v4l2 выделять память в буферы пользователя.

С USERPTR ядро память не выделяет, а использует ту, которую подсунул юзер. Читай исходники ядра.

Сначала я пытался сделать mmap на /dev/null с разными смещениями. Естественно, ничего не получилось.

O_o mmap() на /dev/null?!? o_O

Тогда я решил сделать mmap через виртуальные файлы из shm_open:

Вообще-то можно mmap() делать и без бэкинга на файл. man mmap.

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

Какая такая видеопамять?

Те буферы, которые выделяются с флагом mmap, у меня разделить между разными приложениями не получилось (хотя, возможно, я просто плохо старался).

С USERPTR ядро память не выделяет, а использует ту, которую подсунул юзер. Читай исходники ядра.

Ну, немного не так выразился. Я имел в виду, что при помощи этого флага v4l2 будет использовать буферы пользователя для «сбрасывания» данных.

O_o mmap() на /dev/null?!? o_O

Извиняюсь, «очепятался»: /dev/zero. Почитайте Стивенса - mmap'ировать через /dev/zero удобно.

Да, до меня только сейчас дошло, что можно было сделать mmap на все четыре буфера одним куском, и вычислять адрес буфера как указатель + длина * номер.

Вообще-то можно mmap() делать и без бэкинга на файл. man mmap.

Как? Разве без файла ядро сможет узнать, что две различные области памяти надо синхронизовать?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Slavaz

> То есть, избегайте конструкций вида x++ или x= (exp) ?y:z;

Лучше напишите

x += 1



Слушай,норкоман, иди излагать свои приходы в толксы.

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

> Во многих учебниках, которые я читал, авторы наоборот советуют пользоваться конструкциями с префиксными/постфиксными инкрементами/декрементами, условными операциями вида (a)?b:c и т.п. сокращениями.

Тренарным оператором надо пользоваться осторожно, а пре- и постфиксные операторы - обязательны к применению. Их не просто так «захватили с собой» в javascript.

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

> код будут читать тысячи людей и не все из них смогут сходу прочитать мудрённые конуструкции

Людям, не спосбоным корректно распарсить *ptr++, нет смысла читать рабочий код. Им надо читать (и писать!) учебные примеры.

(а унарные и тринарные операции - это частный случай мудрённых конструкций).


Тем, для кого такие конструкции «мудреные», лучше вообще никакой код не читать. И не писать.

А тебя за раздачу «плохих советов» в /d/ следует хорошенько выпороть.

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