Examen CA/CC 2012-2013
Examen final
Rezolvări
10 iunie 2013
Subiectul 7: (exemplu de răspuns) Folosirea ASLR împiedică rularea de shellcode-uri de pe stivă prin poziționarea stivei în diverse zone din spațiul de adresă; atacatorul nu poate ști (ușor) care este adresa de start a shellcode-ului. Are eficiență în special pe sistemele pe 64 de biți. Pe cele pe 32 de biți se poate folosi brute force.
Subiectul 8: (exemplu de răspuns) Folosirea unui număr mare de thread-uri în cadrul unui proces poate conduce mai rapid la stack overflow. Fiecare thread are stiva proprie, cu dimensiunea fixată la crearea. Dacă se creează prea multe thread-uri, se va ocupa foarte mult stiva. Stivele thread-urilor vor fi apropiate unele de altele astfel că, în cazul unui flux de apeluri mare (apeluri recursive, de exemplu), există riscul ca stiva unui thread să suprascrie stiva altui thread.
Subiectul 11: (exemplu de răspuns) Blocurile/modulele implicate intro diagrama, despre care ar trebui discutat, sunt:
TCP communication
I/O model (async vs threading)
XML parser
Daca le luam de la coada la cap, parserul XML trebuie sa fie lightweight si sa implementeze un subset necesar comunicarii intre client si server.
Modelul de I/O, daca ne uitm la numarul maxim de clienti cerut de specificatii, de 1000, putem sa implementam folosind 1000 de threaduri care asculta pe acelasi socket. Daca insa ne uitam la faptul ca trebuie sa serveasca un numar relativ mare de tranzactii pe secunda, probabil ca un model async I/O ar fi mai potrivit, eliminand context switchurile dintre cele 1000 de threaduri din celalalt model. In cele din urma, probabil ca un model hibrind cu un thread care asculta pe un socket si foloseste async IO si un numar de threaduri care este o functie de numarul de core-uri este cel mai eficient.
Modulul de TCP are o particularitate interesanta. Din starea de listen, la aparitia unei conexiuni noi se creeaza un fd de date. TCPul fiind un protocol de tip stream, sunt doua posibilitati. Daca clientii trimit cereri care au toate nevoie de raspuns, atunci este ok, serverul citeste de pe socket pana cand se poate forma un mesaj XML corect, apoi il trimite mai departe catre procesare si trimite raspunsul inapoi pe aceeasi conexiune de date, dupa care inchid conexiunea. Cazul mai complicat este cand un client poate trimite notificari catre server fara sa astepte raspuns, caz in care este necesar sa se detecteze si sa se delimiteze mai multe requesturi in acelasi buffer. In acest caz este posibil sa apara si desincronizari intre client si server, daca clientul trimite notificari foarte rapid, bufferul de TCP poate sa contina requesturi incomplete, ceea ce complica detectia de mesaje bine formate.
Subiectul 12: (exemplu de răspuns) Aici problema consta in faptul ca nu este scalabil sa stochezi 1,000,000 de fisiere in acelasi director, si nici intro baza de date nu prea are sens sa stochezi ca bloburi toata povestea asta care insumeaza 10TB de date. Asadar, e clar ca fisierele vor trebui stocate pe disc in foldere separate, cel mai bine intro structura arborescenta, care sa se poata intinde pe oricate filesystemuri, deoarece e vorba de o capacitate mai mare decat discurile uzuale. Asadar, blocurile implicate in aceasta solutie sunt doua:
Bloc de identificare
Bloc de stocare
Blocul de identificare se poate implementa cu o tabela simpla in orice sistem de baze de date, care face o mapare de la un identificator unic la o cale de fisier pe disc.
Blocul de stocare are un API simplu, prin care i se cere, pentru un nou fisier de stocat, locul in care se va stoca. Pentru a face asta, trebuie sa stie ce discuri exista in sistem, ce capacitati au si ce filesystem au, pentru a determina un numar optim de intrari in director pentru fiecare dintre ele. Cand se cere un slot pt un fisier nou, sistemul cauta pe volumele existente cel mai bun loc in termeni de spatiu disponibil, intrari in director, etc. Se poate implementa si un load balancer care sa distribuie fisierele cele mai cerute pe discuri diferite, etc.
14 iunie 2013
Subiectul 7: (exemplu de răspuns) Nu este recomandat să se folosească mutex-uri într-un handler de semnal. Dacă se face lock pe mutex și, în programul principal, s-a făcut de asemenea, lock pe mutex, există riscul unui deadlock; handler-u de semnal întrerupe programul principal în timp ce acesta încă are ocupat mutex-ul.
Subiectul 8: (exemplu de răspuns) Un fișier mapat în memorie ocupă pagini fizice (frame-uri) care sunt mapate apoi în spațiuil de adresă al procesului. Un fișier poate fi mapat de mai multe procese, caz în care paginile fizice (frame-urile) aferente pot fi partajate. Scrierile în spațiul de adresă vor ajunge în spațiul fizic și vor fi flushed doar la apeluri specifice sau la închiderea mapării.
Subiectul 11: (exemplu de răspuns) Pentru deschiderea unui fisier e nevoie sa localizam fisierul in dentry - costul este liniar in nr. de fisiere in director in ext2. Idee de baza: ca sa reducem timpul de acces, trebuie sa ne asiguram ca subiectele sunt stocate in directoare care au un numar redus de fisiere.
Pentru a implementa baza de date, avem nevoie de doua lucruri:
o tabela de hash care mapeaza ID-ul fisierului in directorul care il contine. Aceasta tabela va fi stocata in memorie. Presupunand ca numarul de directoare este relativ mic, dimensiunea tabelei este data de nr de fisiere * (dim_id + pointer_nume_director) ~ 8 sau 16 octeti. 10 milioane de fisiere ar ocupa numai 160MB de RAM.
o ierarhie de directoare in care fiecare director are un numar maxim de intrari X (prestabilit).
Se pot folosi multiple structuri ierarhice cu adancime prestabilita. Se creaza astfel mai multe directoare:
un director “direct” care contine un numar de fisiere X
un director “indirect” care contine X alte directoare fiecare cu X fisiere
un director dublu indirect
un director triplu indirect etc.
Se vor folosi directoarele “directe” dupa care cele indirecte, dublu-indirecte, etc.
Cum alegem X? Timpul de acces la un fisier depinde de X si de adancimea structurii de directoare, citirea unui director dureaza: X(logXN + 1).
Stiind N (e.g. 10 milioane), se poate optimiza X alegand valoarea care minimizeaza formula de mai sus.
Subiectul 12: (exemplu de răspuns) Ideea principala este de a trata o cerere de continut cat mai repede si de a inchide conexiunea – astfel numarul de clienti conectati simultan este mic si acest lucru reduce stresul asupra resurselor sistemtului (thread-uri, descriptori).
Se poate folosi un pool de thread-uri; atunci cand vine o cerere noua se aloca cererea unuia din thread-urile din pool. Folosirea pool-ului de thread-uri minimizeaza costurile de startup, si reduc costurile de switching (no tlb flush), insa toti clientii vor folosi acelasi spatiu de adresa – totusi potentialele probleme de securitate par minore (read-only video data).
Se va folosi blocking API pt. citire din sockets – e cea mai usor de folosit. Non-blocking nu prea are sens cu thread-uri. Event based la fel.
Cel mai probabil reteaua va deveni un bottleneck. Din cauza ca folosim un pool de thread-uri nr de thread-uri nu e o problema, si nici cel de descriptori (presupunand ca nu se executa accept atunci cand nu se poate repartiza cererea clientului unui thread).
Lucrări
Nu există sesiune de contestații pentru lucrările de curs. În cazul în care considerați că au fost lipsuri la corectarea lucrării, trimiteți un e-mail lui
Traian.
Lucrare 1
12 martie, 14:05-14:15, EC101, seria CA
13 martie, 17:05-17:15, EC105, seria CC
3CA, nr. 1
Fie P0 procesul părinte al procesului P1, T0 momentul de timp la care P0 execută apelul wait() și T1 momentul de timp la care P1 execută apelul exit(). În ce stare vor fi cele două procese în intervalul (T0, T1) dacă T0<T1?
Răspuns Procesul P0 este în starea WAITING, în așteptarea semnalului de la copil. Procesul P1 poate fi în orice stare, în funcție de codul său, dar va trece, cu siguranță, prin starea RUNNING pentru a putea executa apelul exit().
Știind că apelul write(42, “X”, 1), executat în procesul P, se întoarce cu succes, care este numărul minim de fișiere deschise de procesul P? De ce? Antetul apelului write este write(fd, *buf, count).
Răspuns Numărul minim de fișiere deschise de procesul P este 0, deoarece este posibil ca toate fișierele să fi fost deschise de părintele lui P. Numărul minim de fișiere deschise în procesul P este 1, și anume fișierul cu descriptorul 42, deoarece este posibil ca toți ceilalți descriptori de fișier să fie închiși.
Prezentați un avantaj al mapării spațiului de memorie al kernel-ului în spațiul de adresă al fiecărui proces.
3CA, nr. 2
Fie P0 procesul părinte al procesului P1, T0 momentul de timp la care P0 execută apelul wait() și T1 momentul de timp la care P1 execută apelul exit(). În ce stare vor fi cele două procese în intervalul (T1, T0) dacă T1<T0?
Răspuns Procesul P0 poate fi în orice stare, în funcție de codul său, dar va trece, cu siguranță, prin starea RUNNING pentru a putea executa apelul wait(). Procesul P1 este în starea TERMINATED (zombie), deoarece și-a încheiat execuția și așteaptă să îi fie citită valoarea de ieșire de către părinte.
Fie secvența de pseudocod:
for (i = 0; i < 42; i++)
printf(...);
Care este numărul minim, respectiv numărul maxim de apeluri de sistem din secvența de mai sus?
Răspuns Numărul minim de apeluri de sistem din secvența de mai sus este 0. Dacă printf scrie la terminal, este line buffered și nu se va executa apel de sistem dacă nu se umple buffer-ul sau nu a fost primit caracterul '\n'. Numărul maxim de apeluri de sistem este 42, dacă în fiecare iterație a for-ului se umple buffer-ul sau a fost primit caracterul '\n'.
De ce un proces orfan nu poate deveni zombie?
Răspuns Deoarece un proces orfan este adoptat imediat de init, este imposibil ca el să devină zombie. Acesta execută wait pentru fiecare proces copil al său, care și-a încheiat execuția, împiedicând ca acesta să devină zombie.
3CC, nr. 1
De ce apelul fclose realizează în spate apel de sistem, dar apelul printf nu întotdeauna?
Răspuns Apelul fclose realizează în spate apel de sistem, deoarece închide un fișier, modificând tabela de descriptori din proces. Apelul fclose se mapează pe apelul de sistem close. Apelul printf scrie într-un buffer, iar apelul de sistem write se realizează dacă se umple buffer-ul sau a fost primit caracterul '\n'.
Fie P1 și P2 două procese diferite. Când este posibil ca modificarea cursorului de fișier pentru un descriptor din P1 să conducă la modificarea cursorului de fișier pentru un descriptor din P2?
Răspuns Această situație este posibilă dacă cele două procese au un proces “strămoș” comun și descriptorul de fișier nu a fost închis de niciunul dintre procese. Atunci, modificarea cursorului de fișier pentru un descriptor din P1 poate conduce la modificarea cursorului de fișier pentru același descriptor din P2.
Fie P un proces zombie. Ce procese îl pot elimina din sistem prin apelul wait()?
3CC, nr. 2
În urma unui apel fork() pot rezulta între X și Y procese noi. Ce valori au X și Y?
Răspuns Dacă apelul fork() eșuează nu va fi creat niciun proces nou. Dacă apelul se execută cu succes, va fi creat un proces nou, copil al procesului care a executat fork(). Astfel, pot rezulta între 0 și 1 procese noi. X=0. Y=1.
Care este numărul minim de descriptori de fișier valizi în cadrul unui proces? În ce situație este posibilă această valoare?
Răspuns Numărul minim de descriptori de fișier valizi în cadrul unui proces este 0, în cazul în care un proces închide toți descriptori de fișier, inclusiv stdin, stdout, stderr. Un astfel de proces este numit daemon.
Dați două exemple de resurse care pot aparține unui proces, dar nu pot aparține unui program.
Răspuns Procesul reprezintă o instanță activă activă a unui program. Resursele care pot aparține unui proces, dar nu pot aparține unui program sunt: memoria, CPU-ul, PCB-ul (PID, spațiul de adresă - zonele de date, cod, heap, stivă, tabela de descriptori, masca de semnale, etc.).
Lucrare 2
2 aprilie, 14:05-14:15, EC101, seria CA
3 aprilie, 17:05-17:15, EC105, seria CC
3CA, nr. 1
De ce un planificator echitabil (fair) nu este, în general, productiv (nu oferă un throughput mare)?
De ce este necesară folosirea mutex-urilor, și nu a spinlock-urilor, pentru regiunile critice cu operații de I/O?
Răspuns Regiunile critice cu operații de I/O sunt, de obicei, lungi. De asemenea, operatiile I/O pot duce la blocarea thread-ului, caz în care nu poate fi folosit spinlock-ul.
De ce numărul de pagini virtuale dintr-un sistem este mai mare decât numărul de pagini fizice?
Răspuns Numărul de pagini fizice este limitat de dimensiunea memoriei RAM, în timp ce numărul de pagini virtuale este determinat de numărul de procese. În cazul memoriei partajate de două sau mai multe procese, pot exista mai multe pagini virtuale mapate pe aceeași pagină fizică.
3CA, nr. 2
În ce situație este posibil ca un proces să treacă direct din starea WAITING în starea TERMINATED?
Răspuns Un proces va trece direct din starea WAITING în starea TERMINATED dacă primește un semnal care nu poate fi ignorat sau suprascris, precum SIGKILL sau SIGQUIT, care conduce la terminarea procesului, indiferent de context.
În ce situație este posibilă apariția unui deadlock pe o singură resursă critică?
Răspuns Fie procesul P1 care a acaparat resursa critică și procesele P2, P3, … , Pn care așteaptă eliberarea resursei respective. Un deadlock pe resursa respectivă va apărea dacă procesul P1 nu va elibera resursa critică, fie datorită codului său (nu există instrucțiunea de release/unlock, intră într-un ciclu infinit, etc.), fie deoarece a fost terminat prin semnal SIGKILL.
De ce paginarea ierarhică are un overhead de prelucrare mai mare decât paginarea neierarhică?
Răspuns În cazul paginării neierarhice, numărul paginii virtuale este și indexul în tabela de pagini, deci va exista un singur acces la memorie pentru aflarea paginii fizice. În cazul paginării ierarhice se vor face atâtea accese la memorie, cât numărul de niveluri ierarhice.
3CC, nr. 1
De ce nu mai este folosită planificarea cooperativă în sistemele desktop moderne?
Care este principală sursă de overhead la schimbarea de context între două procese?
Răspuns Principală sursă de overhead la schimbarea de context între două procese este repopularea TLB-ului. În urma unei schimbări de context, intrările din TLB sunt invalidate și este necesară translatarea adreselor virtuale în adrese fizice pe baza tabelei de pagini a noului proces. Acest lucru implică numeroase accesări ale memoriei, la o viteză mult mai mică decât viteza TLB-ului.
De ce nu este necesară eliminarea paginilor de memorie ale kernel-ului din TLB în cazul unei schimbări de context?
3CC, nr. 2
De ce, pe un sistem desktop, de obicei, sunt mai multe procese în starea WAITING decât în starea READY?
Descrieți două diferențe între un mutex și un semafor binar.
Răspuns
Mutexul este folosit pentru acces exclusiv, semaforul binar este folosit pentru sincronizare
Mutexul poate fi eliberat doar de procesul care l-a ocupat, în timp ce orice proces poate incrementa semaforul.
Mutexul este mereu inițializat unlocked, semaforul binar poate fi inițializat la valoarea 0.
Mutexul are un overhead mai mic decât semaforul binar.
Fie un sistem cu paginare ierarhică pe două niveluri, fără TLB. Pot două pagini de memorie virtuală referi aceeași pagină de memorie fizică?
Răspuns Da, două pagini de memorie virtuală pot referi aceeași pagină.
În această situație, în cadrul unui proces, în tabela de pagini, unor intrări diferite (indexate de pagina virtuală A și pagina virtuală B) le corespunde aceeași pagină fizică.
În cazul a două procese este vorba de implementarea mecanismului de shared memory.
Afirmațiile sunt valabile pentru orice tip de paginare: ierarhică, neierarhică, inversată, indiferent de prezența / absența TLB-ului.
Lucrare 3
23 aprilie, 14:05-14:15, EC101, seria CA
24 aprilie, 17:05-17:15, EC105, seria CC
3CA, nr. 1
Presupunând dimensiunea unei pagini de 4096, câte pagini fizice (frame-uri) noi vor fi alocate în urma apelului malloc(6000)? De ce?
Răspuns Apelul malloc folosește demand paging, alocând memorie pur virtuală, fără suport în memoria fizică. Totuși, pentru alocări de dimensiuni mici, este posibil să fie alocate și paginile fizice aferente. Deoarece 4096 < 6000 < 2 * 4096, se vor aloca maxim două pagini.
De ce overhead-ul creării unui nou thread este independent de utilizarea mecanismului copy-on-write?
Răspuns Copy-on-write are sens doar între două tabele de pagini diferite, adică între două procese diferite, întrucât se partajează paginile fizice între pagini virtuale din procese diferite. Thread-urile din același proces partajează tabela de pagină, astfel că overhead-ul creării unui nou thread este independent de copy-on-write.
Cu ce diferă o operație I/O sincronă de o operație I/O blocantă?
3CA, nr. 2
De ce este mecanismul de mapare a fișierelor esențial în rularea proceselor pe sisteme de operare moderne?
Câte fire de execuție va avea un proces multithreaded în urma executării unui apel din familia exec()?
Care sunt cele două argumente ale unei instrucțiuni de tipul IN sau OUT (pentru lucrul cu port-mapped I/O)?
3CC, nr. 1
Fie afirmația “Toate procesele vor genera page fault-uri în urma unui apel din familia exec().” Precizați și justificați valoarea de adevăr a afirmației.
Fie un program multithreaded care rulează pe un sistem uniprocesor cu sistem de operare cu suport multithreaded. În ce situație este mai eficientă folosirea user-level threads în fața kernel-level threads?
De ce nu are sens sortarea operațiilor I/O pentru dispozitive caracter (char devices)?
3CC, nr. 2
Fie afirmația: “Un apel fork() modifică numărul de pagini virtuale și numărul de pagini fizice alocate într-un sistem.” Precizați și justificați valoarea de adevăr a afirmației.
Răspuns Afirmația este adevărată. Numărul paginilor virtuale crește, deoarece apare un nou spațiu de adresă. Crește și numărul de pagini fizice, întrucât se alocă structuri interne nucleului, printre care noua tabelă de pagini pentru procesul copil.
Fie f o funcție care nu este reentrantă. Cum trebuie aceasta apelată pentru a fi thread-safe?
Răspuns Pentru a fi thread-safe, trebuie apelată folosind un mecanism de acces exclusiv. Dacă se pune lock, un singur thread va putea accesa funcția, care devine, astfel, thread-safe.
Prezentați un avantaj al folosirii întreruperilor în fața polling-ului.
Răspuns Polling-ul este un mecanism de tip busy-waiting, deci procesorul va fi ținut ocupat în așteptarea datelor. În schimb, întreruperile nu țin procesorul ocupat, oferind o utilizare mai bună a acestuia.
Lucrare 4
21 mai, 14:05-14:15, EC101, seria CA
22 mai, 17:05-17:15, EC105, seria CC
3CA, nr. 1
Cum se modifică numărul de inode-uri ocupate, respectiv numărul de dentry-uri de pe o partiție în cazul creării unui fișier nou obișnuit (regular file)? Explicați.
Răspuns În cazul creării unui fișier nou obișnuit (regular file), se va crea un dentry nou, în cadrul directorului părinte, și va fi ocupat un inode.
De ce o soluție de virtualizare bazată pe OpenVZ are un overhead mai mic decât VMware Workstation?
De ce NU previne flag-ul NX (No eXecute) atacurile de tipul return-to-libc?
3CA, nr. 2
Fie afirmația “Spațiul ocupat pe disc de un director este constant.” Precizați și justificați valoarea de adevăr a afirmației.
Din ce cauză NU este afectat sistemul gazdă (host) în cazul apariției unei erori fatale la nivelul nucleului unei mașini virtuale VMware Workstation?
Răspuns Deoarece trap-ul generat de eroarea în cauză afectează doar nucleul mașinii virtuale care rulează în user space-ului hypervisor-ului VMware. Hypervisor-ul, adică sistemul gazdă, nu este afectat și poate reporni mașina virtuală.
În ce mod protejează chroot împotriva atacurilor de tip shellcode?
Răspuns În chroot jail nu este adăugat executabil de shell (/bin/bash sau /bin/sh). În acest caz, nu se poate executa un shell dintr-un program chroot-at și, deci, nici un shellcode.
3CC, nr. 1
Cum se modifică numărul de inode-uri, respectiv dentry-uri de pe o partiție în cazul creării unui director nou? Explicați.
De ce este necesară rescrierea instrucțiunilor neprivilegiate în cazul virtualizării folosind VMware Workstation?
De ce NU este stocată valoarea “salt” și în fișierul /etc/passwd?
Răspuns Deoarece valoarea este corelată cu hash-ul, care se stochează în /etc/shadow. De asemenea, din considerente de securitate, nu ar trebui să fie stocată într-un fișier care poate fi citit de orice user.
3CC, nr. 2
Cum se modifică numărul de inode-uri, respectiv dentry-uri de pe o partiție în cazul creării unui hard-link? Explicați.
Răspuns În cazul creării unui hard-link, se va crea un dentry nou, în cadrul directorului părinte, fără a se modifica numărul de inode-uri.
Fie afirmația “Modul în care este tratat un page fault din cadrul unui container OpenVZ este diferit de modul în care este tratat un page fault din cadrul mașinii gazdă.” Precizați și justificați valoarea de adevăr a afirmației.
Răspuns Afirmația este falsă, deoarece toate containerele OpenVZ folosesc același kernel, astfel page fault-urile sunt tratate identic, indiferent de container-ul sursă.
De ce are tehnica ASLR impact redus pe un sistem pe 32 de biți?
Răspuns Tehnica ASLR împiedică atacatorul să cunoască adresa funcției dorite din cadrul zonei de cod prin maparea acestora în puncte aleatoare din spațiul de adresă. Pentru sisteme pe 32 de biți, spațiul virtual nu este suficient de mare, atacatorii putând “căuta” adresa dorită pentru a realiza atacuri de tipul return-to-libc.