LINUX.ORG.RU
ФорумTalks

[ничего не поделаешь, это...] доставляющая история про memcpy


0

2

http://avva.livejournal.com/2323823.html

Для Ъ будут спойлеры:

29 сентября 2010 года. Пользователь "JCHuynh" открывает новый баг на сайте Федоры о том, что 64-битный флэш-плагин от Adobe перестал нормально проигрывать mp3-файлы, выдает все время какой-то треск вместо правильного звука.
В Интеле работают хорошие программисты
А в новой, при копировании от конца к началу, выходит ошибка. Но не всегда, а лишь на некоторых процессорах и в некоторых условиях. И пока что никто этого еще не знает, в конце июня прошлого лета. 

Чипсы это яд. Не жри чипсы, не жри!

★★★★☆
Ответ на: комментарий от Alan_Steel

Совершенно верно. Это значит, что любое изменение undefined поведения не ломает стандарта. Она может вообще отказаться работать, вызвать abort(), и это тоже не будет нарушением.

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

Спасибо, у меня уже не ставится на мой компьютер Win7 x64. То есть ставится, но не живёт, если в системе больше 4G памяти. И после этого вы мне будете рассказывать, как хорошо не соблюдать стандарты.

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

> Криокамеры подтекают, анабиозники проснулись и лезут наружу.

Это ты к чему ляпнул?

это он так неуклюже представился

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

> В стандарте на случай пересечения - undefined, т.е. стандарт поведение не регламентирует вовсе.

Это и есть «регламентированное поведение». И означает оно что так делать нельзя и результат непредсказуем.

«term from the standard is „undefined behaviour“, which means that there is no behaviour that you can rely on.» (c) google

undefined == неопределено. Раз неопределено значит нельзя использовать.

http://en.wikipedia.org/wiki/Undefined_behavior

http://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5...


Прописные истины.

true_admin ★★★★★
()

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

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

3) Тянуть тонны легаси-багов и ворк-эраундов - глупо. Это в конечном итоге приведёт к дикой каше и усложнит поддержку.

4) Быдлокодеры, не осилившие стандартную библиотеку языка Си - не должны вообще писать на Си.

5) Ульрих правильно делает. Стандарт не нарушен. Линуса с его «лишь бы работало» понять можно, но он же считает, что «Stable API is a nonsence». Так что какие проблемы?

ky-san
()
Ответ на: комментарий от Xellos

Я и не против, чтобы оно вызывало abort, забивало память нулями, делало что-то ещё. Но тогда оно должно делать это везде, на любом железе. А оно на одном железе вызывает аборт, а на другом делает что-то еще. Компренде?

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

Любая конкретная реализация ведёт себя каким-то образом. Стандарт это не регламентирует, да и вообще такое поведение возникает лишь в случае ошибки девелопера.

Но всё это не повод, чтобы конкретная реализация на одном железе делала одно, а на другом - другое.

Alan_Steel ★★
()
Ответ на: комментарий от ky-san

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

Там две проблемы: флеш криво написан и глибц ведёт себя неодинаково на разном железе. Предлагается пофиксить второе.

Ульрих правильно делает.

Ульрих там вообще предлагает наплодить тонны несовместимых реализаций memcpy и предоставить выбор между ними девелоперу. Назвать это правильным я не могу.

Alan_Steel ★★
()

Никогда не подозревал, что на Си можно писать такую хрень. И вообще, можно же было написать три варианта memcpy, как у фортовского CMOVE - одна копирует слева направо, другая справа налево (если знаешь, с какой стороны области могут перекрываться), третья копирует всегда корректно.

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

Любая конкретная реализация ведёт себя каким-то образом

а неважно как она себя внутри ведёт, главное что стандартом описывается что функция должна получать на входе и что должно получиться на выходе.

Но всё это не повод, чтобы конкретная реализация на одном железе делала одно, а на другом - другое.

Нонсенс. Внутри glibc, gcc, binutils и очень многих библиотек(особенно мультимедиа) внутри хватает platform-specific code.

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

> Там две проблемы: флеш криво написан и глибц ведёт себя неодинаково на разном железе. Предлагается пофиксить второе.

fix: memcpy ведёт себя одинаково на разном железе *при определённых стандартом параметрах*.

+ я же написал:

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

C чем-то несогласен?

ky-san
()
Ответ на: комментарий от Xenesz

Никогда не подозревал, что на Си можно писать такую хрень

Ты не системный программист.

И вообще, можно же было написать три варианта memcpy

Два уже написано. Можешь предложить третий и послать патч.

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

> Нонсенс.

Речь не о том, что оно реально делает на уровне железа, а о том, как это выглядит для приложения. И это самое поведение должно быть одинаковым. KISS, однако.

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

Ульрих там вообще предлагает наплодить тонны несовместимых реализаций memcpy и предоставить выбор между ними девелоперу. Назвать это правильным я не могу.

в каком месте они одинаковые? У них разные аргументы. В одном случае надо передавать указатели на непересекающиеся буферы, в другом любые.

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

> Ульрих там вообще предлагает наплодить тонны несовместимых реализаций memcpy

Чем несовместимы? Они не работают на описанных стандартом случаях?

ky-san
()
Ответ на: комментарий от Alan_Steel

Речь не о том, что оно реально делает на уровне железа, а о том, как это выглядит для приложения.

Если программист передаёт неправильные аргументы в функцию то это проблемы проблемы программиста а не библиотеки. Ты ещё NULL передай и жалуйся что эти функции не проверяют нулевые указатели. Или то что оба указателя в пределах адресного пространства процессора.

KISS, однако.

Это C а не питон. KISS по скорости бьёт сильно. Поэтому у тебя есть выбор из нескольких языков программирования, выбирай любой.

true_admin ★★★★★
()
Ответ на: комментарий от ky-san

> fix: memcpy ведёт себя одинаково на разном железе *при определённых стандартом параметрах*.

Да, это так. Но ещё лучше вести себя одинаково и в экстремальной ситуации.

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

Я не думаю, что там сознательно кто-то полагался на особенности реализации memcpy. Скорее, это банальный баг из-за недостаточной квалификации разработчиков.

Alan_Steel ★★
()
Ответ на: комментарий от ky-san

> Они не работают на описанных стандартом случаях?

Работают, но результат их работы порою зависит от фазы луны.

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

> Ты ещё NULL передай и жалуйся что эти функции не проверяют нулевые указатели. Или то что оба указателя в пределах адресного пространства процессора.

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

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

Не понял, ты намекаешь на то, что эти функции неправильно хэндлят правильные с точки зрения стандарта аргументы?

ky-san
()
Ответ на: комментарий от Alan_Steel

Но ещё лучше вести себя одинаково

Полная стандартизация? Все пользуется одной ОС, одним софтом? А иначе «одинаково» не получится, будут «нюансы».

и в экстремальной ситуации.

Это не экстремальная ситуация, это _стандартная ситуация_, описанная в стандарте.

true_admin ★★★★★
()
Ответ на: комментарий от ky-san

Я намекаю на то, что в случае неправильных аргументов поведение функции начинает зависеть от совершенно посторонних факторов. Скажем, марки процессора.

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

Я жалуюсь на то, что на разном железе новый memcpy выдаёт разный результат.

Так это РАЗНОЕ железо. Даже если memcpy будет один и тот же, он может работать по-разному. Типичный пример: проги, написанные под i386 могут глючить на amd64. Потому что это C. Добиваться чтобы все функции глючили одинаково на всех платформах это бред. Очевидно что цель в том чтобы все функции следовали стандарту на всех платформах.

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

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

Понятное дело, аргументы ведь неправильные и поведение не определенно. Разве в этом что-то не так?

ky-san
()
Ответ на: комментарий от Alan_Steel

в случае неправильных аргументов поведение функции начинает зависеть от совершенно посторонних факторов

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

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

> зависеть от совершенно посторонних факторов. Скажем, марки процессора.

Так и должно быть, потому что это undefined behaviour.

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

> Никогда не подозревал, что на Си можно писать такую хрень

Ты не системный программист.

Ага, прикинуть палец к носу при копировании в перекрывающую область может только Керниган.

> И вообще, можно же было написать три варианта memcpy

Два уже написано. Можешь предложить третий и послать патч.

Это имеет смысл делать и делается до.

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

Ты, кстати, осознаёшь чего ты просишь? Можно сделать везде проверку всего. В результате получишь python. И даже на питоне народ пишет глючный софт, так что это не панацея. Зато скорость упадёт.

Да, я выше писал что с моей точки зрения несущественна. Но если ты хочешь чтобы всё всегда работало одинаково то придётся приложить совсем другие усилия чем просто добавить проверку. И что это даст? C пойдёт в массы? :)

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

>И это самое поведение должно быть одинаковым.

С описанным в спеках функционалом поведение одинаково.

madcore ★★★★★
()

Есть люди которые следуют стандартам и есть которые не следуют. Как видно у программистов которые им не следуют проблем больше, но они их не всегда решают.

Ты бы, как работодатель, взял бы человека которые в проект сажает такие «оптимизации»?

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

> Ты, кстати, осознаёшь чего ты просишь?

Да, вполне. Я прошу, чтобы вызов функции memcpy с неправильными аргументами или всегда приводил к ошибке или же всегда работал правильно.

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

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

>вызов функции memcpy с неправильными аргументами или всегда работал правильно

/0

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

Тест оверхеда проверки условия.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define SZ (1024 * 64)
char s1[SZ];
char s2[SZ];


void* memmove1(void* _s1, void* _s2, size_t sz) {
	char* s1 = (char*)_s1;
	char* s2 = (char*)_s2;
	if (s2 + sz <= s1 || s1 + sz <= s2) {
		return memcpy(_s1, _s2, sz);
	} else {
		abort();
		return 0;
	}
}

int main(int argc, char** argv)
{
	size_t sz = atoi(argv[1]);
	unsigned nr = atoi(argv[2]);
	int use_memcpy = atoi(argv[3]);

	if (sz > SZ)
		sz = SZ;

	#define test \
	int i, j; \
	for (; nr > 0; nr--) { \
		func(s1, s2, sz); \
		func(s2, s1, sz); \
		func(s1, s2, sz); \
		func(s2, s1, sz); \
		func(s1, s2, sz); \
		func(s2, s1, sz); \
		func(s1, s2, sz); \
		func(s2, s1, sz); \
		func(s1, s2, sz); \
		func(s2, s1, sz); \
	}

	if (use_memcpy) {
		#define func memcpy
		test
		#undef func
	} else {
		#define func memmove1
		test
		#undef func
	}

	return 0;
}

gcc -O3 t.c -o t.bin

dotest() {

	for i in {1..20} ; do
		( time ./t.bin $1 $2 $3 ) 2>&1 | grep user | egrep -o '[0-9]\.[0-9]+s'
	done | awk -Fs '
		{
			if (min=="") { min=$1 };
			if ($1< min) { min=$1 };
		}
		END {
			print min
		}'
}

TOTAL_BYTES=$((0x2000000))

MAX_BLOCK_SIZE=$((0x10000))
BLOCK_SIZE=1

echo block-size count memcpy memmove1

while [[ $BLOCK_SIZE -le $MAX_BLOCK_SIZE ]] ; do
	count=$(($TOTAL_BYTES/$BLOCK_SIZE))
	echo $BLOCK_SIZE $count `dotest $BLOCK_SIZE $count 1` `dotest $BLOCK_SIZE $count 0`
	BLOCK_SIZE=$(($BLOCK_SIZE*2))
done
vadim@host3:~/tmp$ . t.sh | column -t 
block-size  count     memcpy  memmove1
1           33554432  1.809   1.883
2           16777216  5.903   5.930
4           8388608   3.123   3.143
8           4194304   1.557   1.557
16          2097152   0.763   0.767
32          1048576   0.370   0.373
64          524288    0.243   0.227
128         262144    0.150   0.133
256         131072    0.157   0.163
512         65536     0.093   0.080
1024        32768     0.057   0.057
2048        16384     0.047   0.043
4096        8192      0.040   0.033
8192        4096      0.040   0.037
16384       2048      0.043   0.043
32768       1024      0.043   0.043
65536       512       0.033   0.043

Полагаю, в комментариях не нуждается.

geekless ★★
()
Ответ на: комментарий от ky-san

> Понятное дело, аргументы ведь неправильные и поведение не определенно. Разве в этом что-то не так?

Что вы уперлись рогом в этот стандарт и ничего кроме него не видите? У нас есть возможность: 1) оставаясь в рамках стандарта, уменьшить число потенциальных сбоев в клиентском коде и по возможности предотвратить такие сбои в будущем; или 2) у нас есть возможность, оставаясь в рамках стандарта, сидеть и твердить «ничего не слышу, ничего не вижу, что хочу, то ворочу».

Правильный вариант решения очевиден.

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

>Проблема заключена в том, что в стандарте есть две функции делающие одно и тоже, одна из которых кривая. Данный инцидент только в очередной раз показал проблему.

Проблема не в стандарте и не реализациях функций. Проблема в криворуких программистов, которые изучают предмет методом тыка.

В предложенном решении не нужен ни один workaround.

Ты ошибаешься. Нужен workaround для двух типов программ: для таких, как flash, и для таких, которые я приводил в примере выше, где (src-dst)==size/2. Чтобы отличать одни от других, нужно парсить заголовки, как это делают в microsoft.

ttnl ★★★★★
()

ЖЖист отвратителен. Ульрих - молодец. Из-за макак побочного подразделения быдлокодерской конторки Адоб вносить изменения в glibc (которой пользуются, наверно, десятки тысяч программ) категорически НЕЛЬЗЯ. Иначе будет создан прещедент и требования куда менее безобидных хаков польются рекой.

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

[code] void* memmove1(void* _s1, void* _s2, size_t sz) { char* s1 = (char*)_s1; char* s2 = (char*)_s2; if (s2 + sz <= s1 || s1 + sz <= s2) { return memcpy(_s1, _s2, sz); } else { abort(); return 0; } } [/code]

так не пойдет. ты доку-то читал? что там пишут по поводу возвращаемого значения?

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

блин...

void* memmove1(void* _s1, void* _s2, size_t sz) {
   char* s1 = (char*)_s1;
   char* s2 = (char*)_s2;
   if (s2 + sz <= s1 || s1 + sz <= s2) {
      return memcpy(_s1, _s2, sz);
   } else {
      abort();
      return 0;
   }
}

так не пойдет. ты доку-то читал? что там пишут по поводу возвращаемого значения?

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

> что там пишут по поводу возвращаемого значения?

The memcpy function returns the value of s1.

The memmove function returns the value of s1.



В чем проблема?

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

> Ты ошибаешься. Нужен workaround для двух типов программ: для таких, как flash, и для таких, которые я приводил в примере выше, где (src-dst)==size/2. Чтобы отличать одни от других, нужно парсить заголовки, как это делают в microsoft.

лицоладонь.png

Эти два типа программ работают правильно на той реализации memcpy, которая была в коде раньше? Без философствования: да или нет.

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

> В чем проблема?

в том, что у тебя вертается внезапно 0. откуда? зачем? куда его? почему поведение функции не соответствует тому, что заявлено?

взялся писать замену --- пиши нормальную замену, а не бажную кривульку.

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

какая разница? собери у себя и посмотри результат.

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

Ты код читал? В ту ветку управление вообще не попадает, и там воткнут abort, чтобы отловить случай, если мы туда случайно залетим.

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

Дефолтное арчевское ядро.

vadim@host3:/proc$ zcat /proc/config.gz | grep HZ=
CONFIG_NO_HZ=y
CONFIG_HZ=300
geekless ★★
()
Ответ на: комментарий от geekless

>>Без философствования: да или нет.

- Разве твоя мама предупредила меня, что этот мальчик будет у нас обедать? Неужели она так распорядилась?
Малыш постарался ответить как можно более уклончиво, но дружелюбно:
- Во всяком случае, мама считает... что Карлсон...
- Отвечай, да или нет, - прервала его фрекен Бок. - Твоя мама сказала, что Карлсон должен у нас обедать?
- Во всяком случае, она хотела... - снова попытался уйти от прямого ответа Малыш, но фрекен Бок прервала его жестким окриком:
- Я сказала, отвечай - да или нет! На простой вопрос всегда можно ответить «да» или «нет», по-моему, это не трудно.
- Представь себе, трудно, - вмешался Карлсон. - Я сейчас задам тебе простой вопрос, и ты сама в этом убедишься. Вот, слушай! Ты перестала пить коньяк по утрам, отвечай - да или нет?
У фрекен Бок перехватило дыхание, казалось, она вот-вот упадет без чувств. Она хотела что-то сказать, но не могла вымолвить ни слова.
- Ну вот вам, - сказал Карлсон с торжеством. - Повторяю свой вопрос: ты перестала пить коньяк по утрам?
- Да, да, конечно, - убежденно заверил Малыш, которому так хотелось помочь фрекен Бок.
Но тут она совсем озверела.
- Нет! - закричала она, совсем потеряв голову.
Малыш покраснел и подхватил, чтобы ее поддержать:
- Нет, нет, не перестала!
- Жаль, жаль, - сказал Карлсон. - Пьянство к добру не приводит.

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

>Эти два типа программ работают правильно на той реализации memcpy, которая была в коде раньше? Без философствования: да или нет.

Какой же ты трудный. Уже обсуждали. Большинство — да.

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

> Какой же ты трудный. Уже обсуждали. Большинство — да.

Не большинство, а все. Все программы, которые работали нормально ранее, продолжают работать дальше. Потому что имплементация memcpy остаётся прежней.

Что. Здесь. Может. Быть. Не ясно.

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