LINUX.ORG.RU

glibc небезопасна

 , ,


0

7

Привет, ЛОР!

Наткнулся тут на прекрасную историю о том, как использование казалось бы обычных функций из libc может оторвать жопу. И даже Rust не поможет!

Ссылка: https://www.edgedb.com/blog/c-stdlib-isn-t-threadsafe-and-even-safe-rust-didn-t-save-us

Для Ъ:

Функции getenv() и setenv() небезопасны в многопоточных программах. В частности, вызов setenv() во время выполнения getenv() в другом потоке может привести к порче памяти и падению программы. Это особенно релевантно на ARM64, потому как архитектура предоставляет меньше гарантий по части очерёдности выполнения команд.

И если бы это можно было списать на кривые руки авторов статьи, проблемы бы не было. Но тут суть в том, что огромное количество библиотек дёргают getenv() и setenv() под капотом с разными целями, в том числе гнутое gettext и прочие openssl.

Скажи, ЛОР, сишники совсем разучились писать безопасный код? Как с этим жить-то вообще?

UPD:

Подробный пост об этой проблеме: https://www.evanjones.ca/setenv-is-not-thread-safe.html

Прекрасное оттуда:

glibc uses an array to hold pointers to the "NAME=value" strings. It holds a lock in setenv() when changing this array, but not in getenv(). If a thread calling setenv() needs to resize the array of pointers, it copies the values to a new array and frees the previous one. This can cause other threads executing getenv() to crash, since they are now iterating deallocated memory.

То есть, вызов getenv() из glibc потенциально является use-after-free в многопоточной программе и от этого никак нельзя защититься.

★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 3)
Ответ на: комментарий от hateyoufeel

У Rust тоже до жопы проблем, так-то. Упоротая стандартная библиотека, в которую несут всё подряд. Просто лютое безумие с async. Мусорные библиотеки на crates.io. Это всё весьма бесит, да.

Ну тут соглашусь, может быть со временем станет лучше (но помойка на crates.io будет становиться только хуже) или наоборот все станет настолько ужасно что все забудут раст как страшный сон

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

PS/ по хорошему set/getenv должны быть системными вызовами, гарантировать атомарность и усыплять процесс до завершения. На уровне библиотек это не объехать. Это заодно кстати про виндовый реестр, который конечно-же говно, но какой-то аналог нужен :-)

По-хорошему getenv/setenv/environ не нужны вообще. ОС передаёт список env-ов через main, вот и всё. Дальше это уже дело приложения, как с ними работать. У нас же нет функций getarg/setarg и ничего, нормально.

Кстати setenv и /proc/<pid>/environ не работают вместе, что меня, надо сказать, несколько удивило. Изменения, внесённые через setenv не отразились в /proc/<pid>/environ.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 4)
Ответ на: комментарий от hateyoufeel

У Rust тоже до жопы проблем, так-то. Упоротая стандартная библиотека, в которую несут всё подряд. Просто лютое безумие с async.

А что не так со стандартной библиотекой? Мне показалось, что она наоборот слишком куцая. А async там гениален, я всё его хочу заюзать для создания RTOS, он как будто для этого и создавался, очень классно сделан.

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

Это, конечно, проблема, с оставшейся половиной надо что-то делать.

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

Да где это множетство, вызывающее setenv()? То есть какие библиотеки вызывают setenv() и зачем? Пока что здесь не привели примеров. Ведь проблема именно в вызове setenv() после того как процесс стал многопоточным.

ИМХО, в текущих реалиях лучше вобще сделать патч, чтобы процесс падал, если кто-то в нём вызвал setenv() после того, как он стал многопоточным. Чтобы getenv() в многопотоке вёл себя как в однопотоке, нужно для каждого потока нужно делать отдельную копию всех переменных. Чтобы setenv() менял только эти переменные потока того потока, который его вызывал, никак не влияя на соседний поток. Но это не то, что все хотят.

А заменить явные краши из-за «испорченой» памяти на сложно отлавливаемы глюки библиотеки из-за разных значений от getenv(), такое себе решение.

Ну, и я не совсем понимаю, почему Go и пр. цепляются gnulibc, что им мешало форкнуть и делать свой libc? Почему без проблем существуют dietlibc, musl и т.д., но нет golibc?

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

Ну, и я не совсем понимаю, почему Go и пр. цепляются gnulibc, что им мешало форкнуть и делать свой libc? Почему без проблем существуют dietlibc, musl и т.д., но нет golibc?

Упомяну, что во всех системах кроме Linux, которые мне известны, сисколлы не являются стабильным интерфейсом и программы обязаны общаться с ядром через системную libc или её Windows-аналог. В таких системах свой libc сделать невозможно. В OpenBSD ядро вообще проверяет, что в стеке сисколла последним стоит родная libc.

Что касается Linux, там свою libc сделать, конечно, можно. Но надо понимать, что это нетривиальная, мягко говоря, задача. К тому же Go обязан взаимодействовать с нативными библиотеками. Которые слинкованы с системной libc. То бишь нужно ещё и все нативные библиотеки будет перекомпилировать под эту гипотетическую golibc. С таким размахом можно и сразу про gokernel подумать )

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

я всё его хочу заюзать для создания RTOS

уже же сделали - Embassy и RTIC самые живые - и таки да, пушка, удобнее и легче типичной RTOS, оба легче и быстрее FreeRTOS (померяли уже), RTIC ещё и статически защищать от дедлоков умеет

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

В данном случае мне интересен процесс, а не результат. Результат я всё равно буду писать на C и Zephyr, ибо так положено.

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

По-хорошему getenv/setenv/environ не нужны вообще

по чьему такому хорошему ? типичное использование setenv - 1) выставить окружение для дин.библиотек до их загрузки ldload 2) сделать новое окружение для порождаемых процессов.

И если №2 можно сделать и не меняя собственный environ (просто передавать новый при exec), то с №1 никак иначе чем setenv.

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

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

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

Кем положено

Nordic-ом.

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

Во-первых С++ я не люблю. Он очень сложный. Это всё в принципе не моя специализация, но C я хотя бы знаю и могу использовать. Я думал использовать С с классами, но не люблю такой подход, все эти само-ограничения. Да и не вижу большой нужды, С хоть и не идеален, но в целом все его проблемы суть мелочи.

Во-вторых Zephyr хоть и поддерживает C++, но для этого его надо конфигурировать специальным образом, сам он написан на С.

В общем не пытаю я себя сишкой, а вот плюсами скорей всего буду пытать.

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

И если №2 можно сделать и не меняя собственный environ (просто передавать новый при exec), то с №1 никак иначе чем setenv.

Ну прям. Ничего не мешает динамической библиотеке экспортировать void superlib_init(superlib_init_configuration *configuration).

vbr ★★★★★
()

Скажи, ЛОР, сишники совсем разучились писать безопасный код?

Дело в том что и не умели никогда. СИ спроектирован так что не может быть безопасным в многопоточном выполнении по определению. Все существующие примитивы синхронизации неполны из-за того что они не избавляют на 100% от race conditions и UB.

Это как с числами в математике - мы можем вычислять их только до определенной точности, дальше UB. Такая же ситуация и с кодом на Си.

Obezyan
()
Ответ на: комментарий от vbr

Вот интерестно, а под виндой Гошечный os.Getenv() дёргает виндовый getenv_s() или getenv(). getenv() и под виндой, вроде как, не thread-safe, во всяком случае так в документации написано.

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

Дело в том что и не умели никогда. СИ спроектирован так что не может быть безопасным в многопоточном выполнении по определению. Все существующие примитивы синхронизации неполны из-за того что они не избавляют на 100% от race conditions и UB.

Пояснишь, о чём речь? Чем, например, pthread_mutex_lock не полон и от каких гонок от не избавляет?

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от mky

https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L13

func Getenv(key string) (value string, found bool) {
	keyp, err := UTF16PtrFromString(key)
	if err != nil {
		return "", false
	}
	n := uint32(100)
	for {
		b := make([]uint16, n)
		n, err = GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
		if n == 0 && err == ERROR_ENVVAR_NOT_FOUND {
			return "", false
		}
		if n <= uint32(len(b)) {
			return UTF16ToString(b[:n]), true
		}
	}
}

https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable

Под виндой, как и положено, он дёргает родную WinAPI функцию, а не POSIX-овые обёртки. С родной функцией, конечно, проблем никаких нет и быть не может, WinAPI проектировали качественно.

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

Пояснишь, о чём речь? Чем, например, pthread_mutex_lock не полон и от каких гонок от не избавляет?

«Mutex contention» - если несколько тредов ждут освобождения мьютекса и долбятся по pthread_mutex_lock/pthread_mutex_trylock кто за кем будет лочить мьютекс?

Конечно, есть thread priority scheduling в общем виде, но мы заранее не можем со 100% точностью сказать кто за кем должен идти. В итоге это происходит рандомно что ведет к потере значения данных. Особой пикантности добавляет то что эти данные могут использоваться в качестве условий в различных ветвлениях что приводит к UB поведению на высоком уровне.

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

А может ли это привести к тому что такая программа будет крашится? Просто видел такое поведение на многоядерных системах, что если прибить к определенному ядру, то работает без проблем, иначе краш.

vbcnthfkmnth123 ★★★★★
()
Последнее исправление: vbcnthfkmnth123 (всего исправлений: 3)
Ответ на: комментарий от vbcnthfkmnth123

А может ли это привести к тому что такая программа будет крашится?

Конечно, UB есть UB. Правильные данные, записанные не вовремя, становятся неправильными.

Obezyan
()
Ответ на: комментарий от vbr

А что не так со стандартной библиотекой? Мне показалось, что она наоборот слишком куцая.

С одной стороны, да. С другой, в неё тащат очень странные штуки. Типа конверсии из Float в Bool через From.

А async там гениален, я всё его хочу заюзать для создания RTOS, он как будто для этого и создавался, очень классно сделан.

Нет. Во-первых, самая жирная проблема с async в том, что он инфицирует весь код выше по стеку. Если у тебя хоть одна функция async, то всё что её вызывает тоже должно быть async. В результате, у некоторых пакетов есть по две версии: с async и без.

Во-вторых, async не то чтобы улучшает производительность, как выяснилось.

https://bitbashing.io/async-rust.html

В третьих, вся эта возня с Async вылилась в чудовищные костыли типа Unpin, от которых хочется выть иногда.

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

Ты описал не проблему мютекса, а проблему макак, некорректно его использующих. С мютексом всё в порядке и у нормальных людей никаких race от него не получается.

Если в программе несколько тредов ждут один мютекс, и эта программа написана нормальным человеком, это означает что там действительно не важно, кто первый его захватит.

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

Ну, и я не совсем понимаю, почему Go и пр. цепляются gnulibc, что им мешало форкнуть и делать свой libc?

Потому что glibc – это де-факто часть ABI экосистемы Linux. Если Go не будет основан на glibc, то он не сможет использовать OpenGL/Vulkan драйверы например, потому что они загружаются через dlopen и используют glibc.

Почему без проблем существуют dietlibc, musl и т.д., но нет golibc?

Использование другой libc по сути означает создание нового дистрибутива (как например Alpine) и пересборку всех пакетов. Также теряется совместимость с программами с закрытым кодом такие как драйверы Nvidia или нативные игры под Linux.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 3)
Ответ на: комментарий от X512

Изначально всё было нормально, потому что в UNIX не было поддержки потоков, только fork.

но ведь в setlocale/getlocale похожая ситуация (тоже не потокобезопасны), однаке же в POSIX предусмотрена uselocale, которая читает/пишет локаль в привязке к потоку. Почему же тогда не сделали «useenv»?

MirandaUser2
()
Ответ на: комментарий от X512

Ну, golibc может же быть бинарно совместимым с gnulibc и грузить для go-программ этот golibc вместо gnulibc через LD_LIBRARY_PATH/LD_PRELOAD. Обкатали бы, что будет, если заменить setenv() и пр. на thread-safe функции (если такие существуют). Там ведь выяснится, что какие-нибудь библиотеки/функции обращаются к getenv() только один раз при первом вызовые/инициализации и упралять ими через setenv() из другого потока не получится...

mky ★★★★★
()

Кто в вашем цирке главный клоун? (%

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

Не совсем похожая ситуация. Когда поток вызвает uselocale(), понятно, что он хочет изменить локаль для себя. А когда поток вызывает setenv(), то что он хочет — изменить для себя, изменить для всех потоков, изменить для всех потоков, запускаемых после?

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

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

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

когда поток вызывает setenv(), то что он хочет — изменить для себя, изменить для всех потоков, изменить для всех потоков, запускаемых после?

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

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

Чтобы выставить для разных потоков разные значения. Предположим у меня очередь URL’ов на закачку через библиотеку libcurl параллельно в N потоков, причем для каждого URL`а свой RES_OPTIONS.

MirandaUser2
()

На правах наброса: ты ещё про функцию strncpy вспомни;)

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

если несколько тредов ждут освобождения мьютекса и долбятся по pthread_mutex_lock/pthread_mutex_trylock кто за кем будет лочить мьютекс?

Первый попавшийся, ибо сие не важно в программах по типу «тред-пул». Если же важен порядок, то для этого есть мьютексы с условными переменными и семафоры.

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

С getenv понятно, но кто это дёргает setenv?

Там всё тривиально. Как это принято в расте на каждый чих подключать отдельную библиотеку неглядя, они там использовали openssl-probe, в которой кода всего 200 строк и пара циклов, но которая вызывает setenv в непредсказуемое время. Вот и получили, что заслуживали. Понаподключают 100500 кривых библиотек, как в джаваскрипте, а потом удивляются, что у них всё крашится. Пейсатили на расте часто думают, что у них автоматически хороший код, просто потому что у них всё на расте. Это так и читается в посте автора по ссылке. У нас же всё на расте, почему оно падает!?

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

Первый попавшийся, ибо сие не важно в программах по типу «тред-пул»

Вы о себе? Вопрос был про pthread_mutex_lock. С тем же успехом могли написать «а вот в однопоточных программах такого нет».

для этого есть мьютексы с условными переменными и семафоры.

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

Obezyan
()
Ответ на: комментарий от X512

он не сможет использовать OpenGL/Vulkan драйверы например, потому что они загружаются через dlopen

Которая живёт в отдельной libdl.

OpenGL/Vulkan драйверы используют glibc.

А рантайм go-то тут причём? Они используют, они и грузят.

Ну, и я не совсем понимаю, почему Go и пр. цепляются gnulibc

Потому что glibc –

Ответ не правильный. Golang не использует libc и генерирует статические бинарники без зависимостей. Это не Golang использует libc. Это отдельные разработчики отдельных проектов на гошке используют существующие системные библиотеки, которые могут иметь в числе зависимостей libc.

LamerOk ★★★★★
()
Последнее исправление: LamerOk (всего исправлений: 2)
Ответ на: комментарий от LamerOk

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

И из-за привого формата исполняемых файлов ELF несколько разных версий libc не могут быть загружены в один процесс. В Windows без проблем можно загрузить несколько версий libc.

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

Спрашивайте у тех, кто нашёл вызовы

Ты обдолбанный, чтоле? Какое отношение те, кто нашёл вызовы, имеют к формату elf-файла? Какое отношение формат elf-файла имеет к тому, что golang не использует libc? Или ты просто решил посоперничать с чатгопотой по генерации бессвязного бреда на ключевые слова?

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

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

Ну так ты просвети нас, как решить producer-consumer problem с одним только мьютексом в нормальном безопасном языке.

Ты же не клоун, как растоманы в ОП-посте, верно?

shkolnick-kun ★★★★★
()
Ответ на: комментарий от rtxtxtrx

Не совсем понял, что ты написал, сорри. Что ты понимаешь под перезапуском?

vbr ★★★★★
()
Ответ на: комментарий от shkolnick-kun

как решить producer-consumer problem с одним только мьютексом в нормальном безопасном языке.

define безопасный язык в многопотоке из коробки, я таких не знаю. Самое близкое из того что видел безопасное - NVIDIA CUDA, правда выполняется он на GPU, а не на CPU и там вообще отсутствует примитив синхронизации тредов грида при том что вся архитектура заточена на параллельную обработку данных в тредах. Это сделано намеренно, чтобы изначально архитектура программы не имела встроенных зависимостей между данными и независимыми блоками (тредами), а синхронизация сделана ожиданием завершения работы остальных блоков занятых обработкой данных, а не блокировками.

Возвращаясь к CPU, в общем виде есть всего два основных подхода:

  1. CAS (сравнение с обменом) с алгоритмом сравнения учитывающим тип и назначение данных
  2. спинлоки, если первый пункт невозможен по каким-то причинам

Оба варианта это попытки уйти от явных блокировок. Часто применяются в виде ассемблерных вставок, GCC понимает, базовая реализация есть в IntelTBB.

Большинство подобное не используют ввиду незнания или нежелания настолько усложнять логику, вроде работает и так, и ладно. Иногда наваливают дополнительный слой абстракции в виде использования языка с виртуальной машиной чтобы и надеяться что она сделает все верно.

Ты же не клоун, как растоманы в ОП-посте, верно?

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

Скорее всего это просто люди ничего по-настоящему серьезного и многопоточного не писавшие, оттого и витающие в иллюзиях «ну не могу же я писать на такой какашке! Это что-то с Обезъяном не так!».

Obezyan
()
Ответ на: комментарий от vbr

А под unix, как-то всё ещё веселее, или я не правильно понял код. https://github.com/golang/go/blob/master/src/syscall/env_unix.go

У меня получается, что go в начале копирует себе все переменные и как-то их там у себя держит и лок для доступа заводит. При изменении переменной из go будет дёрнут setenv_c(), которая, если загружен cgo, дёрнет setenv(), который Си'шный из libc? А getenv_c() или чего-то подобного я не увидел, получается, что если Си-код изменит переменную среды через setenv(), то go-код её не увидит.

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

или я не правильно понял код.

Это.

При изменении переменной из go будет дёрнут setenv_c(),

Где ты вообще его увидел? Там ASCII по пробелам написано runtimeSetenv , который syscall.runtimeSetenv - обёртка для конкретной платформы. На линуксе это сразу обращение в ядро.

получается, что если Си-код изменит переменную среды через setenv(), то go-код её не увидит.

Как у тебя получается смотреть в гошный код и видеть фигу там, где всё прогрессивное человечество, я бы даже сказал «вся индустрия» видит

func Setenv(key, value string) error {
	...
	runtimeSetenv(key, value)
	...

?

Как научится настолько не понимать go-шный код? Этому где-то учат или это природный дар?

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

Не я начинал эту тему с go и setenv.

Да, но ты зачем-то принёс в неё нерелейтедный формат эльфа, а на попытки объяснить, зачем ты это сделал, начал переводить стрелки.

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

А этот код: https://github.com/golang/go/blob/master/src/runtime/runtime.go

//go:linkname syscall_runtimeSetenv syscall.runtimeSetenv
func syscall_runtimeSetenv(key, value string) {
	setenv_c(key, value)
	if key == "GODEBUG" {
		p := new(string)
		*p = value
		godebugEnv.Store(p)
		godebugNotify(true)
	}
}

На линуксе это сразу обращение в ядро.

В какое ядро обращается syscall.runtimeSetenv, и, главное, зачем?

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

получается, что если Си-код изменит переменную среды через setenv(), то go-код её не увидит.

Не распарсил твой пост сразу корректно. Похоже на то, да.

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

Изменения, внесённые через setenv не отразились в /proc/<pid>/environ

Да, никто не хочеть дёргать prctl(PR_SET_MM_ENV_START,). Не знаю, почему, в glibc не стали делать, если хранят всё одним куском. А в musl, если там каждая переменная может быть в отдельном malloc(), вобще не возможно. Ядро отображает только один участок памяти в /proc/PID/environ.

getenv/setenv/environ не нужны вообще.

А GetEnvironmentVariable() нужно? Если считать, что

WinAPI проектировали качественно.

А без char** environ не получится откуда-нибудь из библиотеки сделать нормальный fork()+exec(). Ну, чтобы всякие переменные среды, о которых библиотека не знает, передались дочернему процессу. Или что, добавлять во все функции аргумент "** env", чтобы до библиотеки доходило, что передали main()? Или вводить gelenvall()?

P.S. Из буквоедства, ядро не передаёт в main() список env, это libc до вызова main() парсит аргумент программы после последнего (arcv[argc]), копирует в другую память, заносит значение в **environ и **envp. А ядро, как отображало этот кусок памяти, куда исходно сложило переменные среды, в /proc/PID/environ, так и отображет.

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

А без char** environ не получится откуда-нибудь из библиотеки сделать нормальный fork()+exec().

Так это fork()+exec() не нужны. В Windows, собственно, fork() и нет.

Без глобального char **environ как раз отлично можно сделать posix_spawn() (или execvpe, что аналогично, но не в POSIX) и передать туда указатели на argv и envp.

P.S. Из буквоедства, ядро не передаёт в main() список env, это libc до вызова main() парсит аргумент программы после последнего (arcv[argc]), копирует в другую память, заносит значение в **environ и **envp. А ядро, как отображало этот кусок памяти, куда исходно сложило переменные среды, в /proc/PID/environ, так и отображет.

Это детали реализации конкретного ядра, на них вообще плевать.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

и передать туда указатели на argv и envp.

Чтобы туда передать envp, нужно во все функции по дороге до точки вызова posix_spawn()/execle()/execvpe() добавлять аргумент. Иначе негде будет взять envp. И отваливается execl()/execp(), так как они через environ превращаются в execle()/execvpe(). Вобще весь код переписывать.

Это детали реализации

Это относится к /proc/PID/environ, с чего началось моё сообщение. Что, даже если делать setenv() на уже существующую переменную и без изменения длины, что как-бы не требует выделения памяти, всё одно в /proc/PID/environ изменений не будет, там другая область памяти.

mky ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.