💾 Archived View for gemini.strahinja.org › blog › 2022-08 › 20220831.gmi captured on 2023-07-22 at 16:25:25. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-06-16)

-=-=-=-=-=-=-

Важност прототипова

Радећи на писању mkfile-а за Neomutt, сусрео сам се са необичним проблемом. При покретању, Neomutt је крахирао уз SIGSEGV, а испитивањем у GDB-у установио сам да до грешке долази у фајлу gui/terminal.c, у линијама 62-63:

char *tcaps = tigetstr("tsl");
if (tcaps && (tcaps != (char *) -1) && *tcaps)

Наиме, вредност показивача tcaps је у моменту segmentation fault-а 0xfffffffff75de85c, што није адреса којој програми смеју приступати. Даље испитивање ме је довело до функције tigetstr(3) из NetBSD-curses-а:

const char *
ti_getstr(const TERMINAL *term, const char *id)
{
	ssize_t ind;
	size_t i;
	TERMUSERDEF *ud;


=> https://git.sr.ht/~strahinja/galeb-mkfiles/tree/master/item/neomutt/mkfile	mkfile

	_DIAGASSERT(term != NULL);
	_DIAGASSERT(id != NULL);


	ind = _ti_strindex(id);
	if (ind != -1)
		return term->strs[ind]; // <-- ово се враћа...


	/* ... */
}


char *
tigetstr(const char *id)
{


	_DIAGASSERT(id != NULL);
	if (cur_term != NULL)
	{
		char* ret = ti_getstr(cur_term, id);
		return __UNCONST(ret); // <-- ... и прослеђује
				       //    макроу __UNCONST
	}
	return (char *)CANCELLED_STRING;
}


Адреса показивача term->strs[ind] који се враћа из интерне функције ti_getstr у функцију tigetstr je 0x7ffff75de85c, што је исправна адреса показивача, и у GDB-у се приказује као знаковна ниска. Она се после cast-овања макроом __UNCONST:

#define __UNCONST(a)   (void *)(intptr_t)(a)

враћа и додељује већ поменутом показивачу tcaps у функцији mutt_ts_capability у Neomutt-у.

Међутим, ту долази до проблема: 0x7ffff75de85c одједном постаје 0xfffffffff75de85c. Зашто?

Испрва сам помислио да је кривац различита врста кода у односу на позицију (PIC - Position Independent Code), али поновно компајлирање NetBSD-curses-а и Neomutt-а уз -fPIC није ништа променило.

Затим сам покушао да направим мали тест програм, који би иницијализовао NetBSD-curses и позвао проблематичну функцију tigetstr. На моје изненађење, док је Neomutt проузроковао segmentation fault, тест програм је радио без проблема! Додавање још кода из Neomutt-а тест програму није променило ситуацију.

Оно што ми је коначно дало идеју шта је узрок проблема је следећи одговор на питање на StackOverflow-у:

channelGet() is assumed to return int (due to lack of prototype) [истакао СР], so the result is truncated to 0xf7fed1a0 then it is cast to a 64-bit pointer, so gets sign-extended to 0xfffffffff7fed1a0

„Претпоставља се да функција враћа int, због недостатка прототипа, па је резултат скраћен. Онда се cast-ује у 64-битни показивач, коме се продужава знак.“ Па наравно, недостатак прототипа! Функција tigetstr захтева укључивање заглавља term.h, које није било укључено са том путањом, већ као ncurses/term.h. Што се тиче самог прототипа, у овом случају он је сасвим једноставан, без struct-ова, али је његов недостатак ипак проузроковао SIGSEGV!

одговор на питање на StackOverflow-у

————————————————————

Епилог: Neomutt ипак захтева функције које недостају NetBSD-curses-у, па ћу за његово коришћење по свој прилици морати или да их сам напишем, или да пређем на гломазни ncurses, али сам из целе ствари имао прилике да научим нешто ново.