Byla vydána nová major verze 5.0.0 svobodného multiplatformního nástroje BleachBit (GitHub, Wikipedie) určeného především k efektivnímu čištění disku od nepotřebných souborů.
Na čem pracují vývojáři webového prohlížeče Ladybird (GitHub)? Byl publikován přehled vývoje za duben (YouTube).
Provozovatel čínské sociální sítě TikTok dostal v Evropské unii pokutu 530 milionů eur (13,2 miliardy Kč) za nedostatky při ochraně osobních údajů. Ve svém oznámení to dnes uvedla irská Komise pro ochranu údajů (DPC), která jedná jménem EU. Zároveň TikToku nařídila, že pokud správu dat neuvede do šesti měsíců do souladu s požadavky, musí přestat posílat data o unijních uživatelích do Číny. TikTok uvedl, že se proti rozhodnutí odvolá.
Společnost JetBrains uvolnila Mellum, tj. svůj velký jazykový model (LLM) pro vývojáře, jako open source. Mellum podporuje programovací jazyky Java, Kotlin, Python, Go, PHP, C, C++, C#, JavaScript, TypeScript, CSS, HTML, Rust a Ruby.
Vývojáři Kali Linuxu upozorňují na nový klíč pro podepisování balíčků. K původnímu klíči ztratili přístup.
V březnu loňského roku přestal být Redis svobodný. Společnost Redis Labs jej přelicencovala z licence BSD na nesvobodné licence Redis Source Available License (RSALv2) a Server Side Public License (SSPLv1). Hned o pár dní později vznikly svobodné forky Redisu s názvy Valkey a Redict. Dnes bylo oznámeno, že Redis je opět svobodný. S nejnovější verzí 8 je k dispozici také pod licencí AGPLv3.
Oficiální ceny Raspberry Pi Compute Modulů 4 klesly o 5 dolarů (4 GB varianty), respektive o 10 dolarů (8 GB varianty).
Byla vydána beta verze openSUSE Leap 16. Ve výchozím nastavení s novým instalátorem Agama.
Devadesátková hra Brány Skeldalu prošla portací a je dostupná na platformě Steam. Vyšel i parádní blog autora o portaci na moderní systémy a platformy včetně Linuxu.
Lidi dělají divné věci. Například spouští Linux v Excelu. Využít je emulátor RISC-V mini-rv32ima sestavený jako knihovna DLL, která je volaná z makra VBA (Visual Basic for Applications).
Aktuální vývojová verze jádra je 3.16-rc4 vydaná 6. července.
Stabilní jádra: Greg Kroah-Hartman 3. července oznámil, že 3.14 bude příštím dlouhodobě udržovaným stabilním jádrem; bude jej udržovat přibližně do srpna 2016.
Stabilní jádra 3.15.4, 3.14.11, 3.10.47 a 3.4.97 vyšla 6. července, 9. července je následovaly verze 3.15.5, 3.14.12, 3.10.48 a 3.4.97.
Jinými slovy, jak by vypadalo hlášení chyby od uživatele?
Je důležité takto přemýšlet, protože za rok někdo, o němž jsme nikdy neslyšeli, možná bude pročítat hlášení o chybě od uživatele a bude přemýšlet, jestli to backportování této opravy vyřeší. A jsou i další důvody.
Dochází mi, že kdyby ses tak zoufale nenudil, tak bys mě nikdy nežádal o to, abych hackoval skript v Perlu!
My se nesnažíme programovat defenzivně, my děláme jen a pouze logické věci.
-- Eric Dumazet
Ve zprávě oznamující vydání realtime linuxového jádra 3.14.10-rt7 Thomas Gleixner znovu zopakoval, že problémy s financováním, které realtime Linux sužují (na což opět upozornil na loňském Real Time Linux Workshopu), se ještě zhoršily. Byla tu snaha získat zdroj peněz, ale nijak to nedopadlo. Pokud se to nezmění, tak má Gleixner v plánu omezit vývoj a dostat kód do upstreamu. Po mé poslední přednášce o stavu preempt-RT na japonském LinuxConu mi Linus řekl: „To bylo ještě smutnější, než jsem se obával.“ Hlavní řada jádra se za posledních 10 let dočkala řady užitečných věcí od preempt-RT a v upstreamu ještě zbývá udělat mnoho, než bude preempt-RT plně integrován, což by jistě opět zlepšilo stav linuxového jádra.
Systémová volání jsou hlavním mechanismem pro interakci mezi programy v uživatelském prostoru a linuxovým jádrem. Vzhledem k jejich důležitosti nepřekvapí, že jádro obsahuje celou řadu mechanismů zajišťujících, že je možné systémová volání implementovat pro různé architektury obecně a je možné je uživatelskému prostoru nabízet efektivním a konzistentním způsobem.
Autor tohoto textu pracoval na portování bezpečnostního mechanismu Capsicum z FreeBSD na Linux a jelikož k tomuto přísluší přidání několika systémových volání (včetně poněkud neobvyklého volání execveat()), musel prozkoumávat, jak jsou implementovány. Výsledkem toho je první z dvojice článků popisujících podrobnosti jejich implementace. V tomto článku se budeme zaměřovat na nejčastější případ: mechanismus obyčejného systémového volání (read()) spolu s tím, co programům na x86_64 umožňuje jeho spuštění. Druhý článek se přesune k méně obvyklým voláním a jiným způsobům jejich spouštění.
Systémové volání se od běžného volání funkce liší tím, že volaný kód se nachází v jádře. Pro přechod procesoru do ring 0 (privilegovaného režimu) je nutné použít speciální instrukce. Navíc je spouštěný jaderný kód označen číslem volání namísto adresy funkce.
Systémové volání read() je ideální jako první ukázka při prozkoumávání fungování systémových volání v jádře. Je implementováno v fs/read_write.c jako krátká funkce, která nechává většinu práce na vfs_read(). Z pohledu spuštění je nejzajímavější stránkou tohoto kódu to, jak je funkce definována pomocí makra SYSCALL_DEFINE3(). Na první pohledu není hned jasné, jak se funkce jmenuje.
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; /* ... */
Tato makra SYSCALL_DEFINEn() jsou standardním způsobem, jak se v jaderném kódu definuje systémové volání, kdy přípona n označuje počet argumentů. Definice těchto maker (v include/linux/syscalls.h) vytváří pro každé volání dva kusy kódu:
SYSCALL_METADATA(_read, 3, unsigned int, fd, char __user *, buf, size_t, count) __SYSCALL_DEFINEx(3, _read, unsigned int, fd, char __user *, buf, size_t, count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; /* ... */
První z nich – SYSCALL_METADATA() – sestavuje sadu metadat o systémových voláních pro účely trasování. Expanduje se pouze tehdy, když je při sestavování jádra definováno CONFIG_FTRACE_SYSCALLS, a jeho expanze generuje definice dat popisujících systémová volání a jejich parametry. (Více o nich se dočtete na jiné stránce.)
Část nazvaná __SYSCALL_DEFINEx() je zajímavější, protože se v ní skrývá implementace systémového volání. Jakmile jsou různé vrstvy maker a typových rozšíření GCC expandovány, začíná být výsledný kód zajímavý:
asmlinkage long sys_read(unsigned int fd, char __user * buf, size_t count) __attribute__((alias(__stringify(SyS_read)))); static inline long SYSC_read(unsigned int fd, char __user * buf, size_t count); asmlinkage long SyS_read(long int fd, long int buf, long int count); asmlinkage long SyS_read(long int fd, long int buf, long int count) { long ret = SYSC_read((unsigned int) fd, (char __user *) buf, (size_t) count); asmlinkage_protect(3, ret, fd, buf, count); return ret; } static inline long SYSC_read(unsigned int fd, char __user * buf, size_t count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; /* ... */
Nejprve si všimněme toho, že implementace systémového volání má ve skutečnosti název SYSC_read(), ale je statická, a tudíž nepřístupná mimo tento modul. Namísto toho je tu zvenčí viditelná obalující funkce nazvaná SyS_read(), na kterou míří alias sys_read(). Při bližším pohledu na tyto aliasy si lze všimnout rozdílu v typech argumentů – sys_read() očekává explicitně deklarované typy (např. char __user * pro druhý argument), zatímco SyS_read() jen hromadu (long) integerů. Pokud si dohledáme původ tohoto, zjistíme, že long verze zajišťuje to, že 32bitové hodnoty jsou správně znaménkově rozšířeny na některých 64bitových platformách, čímž se předchází dávné zranitelnosti.
Poslední věcí, kterou můžeme na obalení SyS_read() zaznamenat, je direktiva asmlinkage a volání asmlinkage_protect(). FAQ na Kernel Newbies ochotně vysvětluje, že asmlinkage znamená, že funkce by měla očekávat své argumenty na zásobníku namísto v registrech a obecná definice asmlinkage_protect() vysvětluje, že se používá k zabránění tomu, aby kompilátor předpokládal, že může bezpečně opětovně používat tyto části zásobníku.
S definicí sys_read() (tou, která má přesné typy) souvisí deklarace v include/linux/syscalls.h, což umožňuje jinému jadernému kódu volat implementaci tohoto systémového volání napřímo (což se děje na řadě míst). Volání systémových volání z jiných míst v jádře se obecně nedoporučuje a moc často se nevidí.
Hledání toho, kdo sys_read() volá, nás také navede k tomu, jak se uživatelský prostor dostane až do této funkce. U „obecných“ architektur, které nemají vlastní řešení, nabízí include/uapi/asm-generic/unistd.h položku odkazující na sys_read:
#define __NR_read 63 __SYSCALL(__NR_read, sys_read)
Toto definuje obecné číslo volání __NR_read (63) pro read() a používá makro __SYSCALL() pro spojení tohoto čísla se sys_read() způsobem specifickým pro platformu. Například arm64 používá hlavičkový soubor asm-generic/unistd.h pro naplnění tabulky, jež mapuje čísla volání na ukazetele na funkci s implementací.
Budeme se ale soustřeďovat na architekturu x86_64, která tuto obecnou tabulku nepoužívá. x86_64 místo toho definuje vlastní mapování v arch/x86/syscalls/syscall_64.tbl, které má položku pro sys_read():
0 common read sys_read
Zde je vidět, že read() na x86_64 má číslo volání 0 (nikoliv 63) a má společnou (common) implementaci pro obě ABI na x86_64, jmenovitě sys_read(). (O různých ABI si povíme v dalším článku.) Skript syscalltbl.sh generuje soubor arch/x86/include/generated/asm/syscalls_64.h z tabulky syscall_64.tbl, především pak generuje spouštění makra __SYSCALL_COMMON pro sys_read(). Tento hlavičkový soubor je pak zase použit k naplnění tabulky systémových volání sys_call_table, jež je klíčovou datovou strukturou mapující čísla volání na funkce sys_name().
Nyní se podíváme na to, jak programy v uživatelském prostoru spouštějí systémová volání. Toto už z principu závisí na platformě, takže se zbytek tohoto textu bude zaměřovat na architekturu x86_64 (na jiné architektury x86 se podíváme příště).
V předchozí části článku jsme narazili na tabulku s ukazateli na funkce systémových volání; tabulka pro x86_64 vypadá asi tak následovně (za použití rozšíření GCC pro inicializací polí, které zajišťuje, že chybějící položky budou odkazovat na sys_ni_syscall()):
asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { [0 ... __NR_syscall_max] = &sys_ni_syscall, [0] = sys_read, [1] = sys_write, /*... */ };
Pro 64bitový kód je k této tabulce přistupováno z arch/x86/kernel/entry_64.S ze vstupního bodu system_call; pro volbu správné položky v poli se používá registr RAX, pak je funkce zavolána. Blíže k začátku funkce se používá makro SAVE_ARGS k uložení různých registrů na zásobník, aby to odpovídalo direktivě asmlinkage, kterou jsme viděli dříve.
Pokud se posuneme dál, uvidíme, že vstupní bod system_call se používá v syscall_init(), což je funkce volaná v raných fázích inicializace jádra:
void syscall_init(void) { /* * LSTAR and STAR live in a bit strange symbiosis. * They both write to the same internal register. STAR allows to * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip. */ wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32); wrmsrl(MSR_LSTAR, system_call); wrmsrl(MSR_CSTAR, ignore_sysret); /* ... */
Instrukce wrmsl zapíše hodnotu do registrů specifických pro model (Model Specific Register; MSR); v tomto případě je adresa obecné obsluhy system_call zapsána do registru MSR_LSTAR (0xc0000082), což je registr používaný při obsluze instrukce SYSCALL.
A toto nám dokončuje cestu z uživatelského prostoru do jaderného kódu. Standardní ABI pro spouštění systémových volání na x86_64 nám říká dát číslo volání (0 pro read) do registru RAX, další parametry pak do konkrétních registrů (RDI, RSI, RDX pro první tři) a pak spustit instrukci SYSCALL). Instrukce způsobí přechod procesoru do ring 0 a spuštění kódu, na který ukazuje registr MSR_LSTAR – konkrétně tedy system_call. Kód v system_call uloží registry na zásobník jádra a zavolá ukazatel na funkci v položce dle RAX v tabulce sys_call_table – jmenovitě sys_read(), což je tenkým obalením pro skutečnou implementaci v SYSC_read().
Teď, když jsme viděli standardní implementaci systémových volání na nejběžnější platformě, máme větší šanci porozumět tomu, co se děje na ostatních architekturách. Tomu se budeme věnovat příště.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
Je důležité takto přemýšlet, protože za rok někdo, o němž jsme nikdy neslyšeli, možná bude pročítat hlášení o chybě od uživatele a bude přemýšlet, jestli to backportování této opravy vyřeší. A jsou i další důvody.
Svatá slova… Ideální commit message od opravy chyby by měl obsahovat tři důležité informace: co bylo špatně z hlediska kódu, jak se to projevovalo navenek a jak se to opravilo (je-li to relevantní, tak navíc i informaci, kdy chyba vznikla). Až příliš často tam bohužel bývá jen to první nebo třetí, v lepším případě oboje.
userspace API se mění ještě častějiblábol