Zadanie 3: modyfikacja jądra

Data ogłoszenia: 15.05.2018

Termin oddania: 12.06.2018 (ostateczny 26.06.2018)

Materiały dodatkowe

Wprowadzenie

Do śledzenia programów użytkownika w systemie Linux służy syscall ptrace. Ma on (m.in.) następujące możliwości:

  • podpięcie do procesu śledzonego (targetu)
  • monitorowanie i przechwytywanie sygnałów przychodzących do targetu
  • pisanie i czytanie pamięci oraz stanu rejestrów targetu
  • zatrzymywanie i wznawianie pracy targetu, w kilku trybach:
    • PTRACE_CONT: target jest wznawiany do otrzymania sygnału lub ręcznego zatrzymania
    • PTRACE_SYSCALL: jak wyżej, ale target jest zatrzymywany przed wejściem do syscalla oraz po wyjściu z niego
    • PTRACE_SINGLESTEP: target jest wznawiany, wykonuje jedną instrukcję procesora, po czym jest zatrzymywany
    • PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP: jak PTRACE_SYSCALL i PTRACE_SINGLESTEP, ale syscalle wywoływane przez target nie są wykonywane przez kernel

Niestety, mechanizm ten jest bardzo prymitywny, i nie pozwala na wykonywanie wielu operacji bardzo przydatnych przy pisaniu debuggerów. Jednym z przykładów jest manipulacja otwartymi plikami czy mapowaniami w procesie (np. nie da się utworzyć nowego mapowania). Aby obejść to ograniczenie, debuggery muszą:

  • znaleźć jakieś miejsce w segmencie kodu targetu
  • zachować jego zawartość
  • wstrzyknąć w to miejsce instrukcję wywołującą syscall
  • zachować obecny stan rejestrów
  • wstawić do rejestrów numer i parametry syscalla robiącego to, co debugger chce (np. mmap)
  • ustawić wskaźnik instrukcji tak, aby wskazywał na wstrzyknięty kod
  • uruchomić PTRACE_SINGLESTEP
  • wyciągnąć wynik syscalla z rejestru
  • odtworzyć stan rejestrów
  • odtworzyć nadpisane miejsce w segmencie kodu

Mechanizm ten ma pewne wady.

Zadanie

Dopisać do syscalla ptrace operacje pozwalające na manipulację otwartymi plikami i mapowaniami innych procesów:

  • PTRACE_REMOTE_MMAP: tworzy nowe mapowanie w zdalnym procesie. addr jest ignorowane, data wskazuje na strukturę ptrace_remote_mmap, zawierającą parametry do wywołania:

    • addr: adres pod którym ma być utworzone mapowanie, bądź 0 aby jądro automatycznie wybrało adres (jak w zwykłym mmap). Po udanym wywołaniu, jądro zapisze w tym polu wybrany adres.
    • length: jak w zwykłym mmap.
    • prot: jak w zwykłym mmap.
    • flags: jak w zwykłym mmap.
    • fd: deskryptor pliku, który ma być zmapowany. Deskryptor pliku jest szukany w plikach procesu wywołującego ptrace, a nie procesu w którym będzie tworzone mapowanie. Jeśli flags zawiera MAP_ANONYMOUS, to pole jest ignorowane.
    • offset: jak w zwykłym mmap.

    Jeśli tworzenie mapowania się udało, wynikiem tego wywołania jest 0, a adres mapowania jest zwracany w polu addr przekazanej struktury. Jeśli się nie udało, zwracany jest kod błędu, a pole addr nie jest modyfikowane.

  • PTRACE_REMOTE_MUNMAP: usuwa mapowanie w zdalnym procesie. addr jest ignorowane, a data wskazuje na strukturę ptrace_remote_munmap, zawierającą parametry wywołania. Wartość zwracana i parametry są takie, jak przy zwykłym munmap.

  • PTRACE_REMOTE_MREMAP: jak PTRACE_REMOTE_MMAP, ale wywołuje mremap. Jeśli wywołanie się uda, wynikiem jest 0, a adres mapowania jest zwracany w polu new_addr przekazanej struktury.

  • PTRACE_REMOTE_MPROTECT: jak wyżej, ale dla mprotect.

  • PTRACE_DUP_TO_REMOTE: zachowuje się jak dup, ale tworzy nowy deskryptor w zdalnym procesie. addr jest ignorowany, a data wskazuje na strukturę ptrace_dup_to_remote:

    • local_fd: deskryptor pliku w procesie wywołującym ptrace, który chcemy zduplikować do procesu śledzonego
    • flags: flagi nowego deskryptora. Jedyną zdefiniowaną flagą jest O_CLOEXEC.

    Wynikiem wywołania jest deskryptor utworzony w zdalnym procesie, lub kod błędu.

  • PTRACE_DUP2_TO_REMOTE: zachowuje się jak dup3, ale tworzy nowy deskryptor w zdalnym procesie. Jak wyżej, ale pobiera dodatkowy parametr:

    • remote_fd: deskryptor pliku, który ma być utworzony (bądź zamieniony) w zdalnym procesie.
  • PTRACE_DUP_FROM_REMOTE: zachowuje się jak dup, ale duplikuje deskryptor ze zdalnego procesu do procesu wywołującego ptrace. data wskazuje na strukturę ptrace_dup_from_remote:

    • remote_fd: zdalny deskryptor pliku do zduplikowania.
    • flags: jak w PTRACE_DUP_TO_REMOTE.

    Wynikiem wywołania jest nowy deskryptor w procesie wywołującym, bądź kod błędu.

  • PTRACE_REMOTE_CLOSE: zamyka deskryptor pliku w zdalnym procesie. data wskazuje na strukturę ptrace_remote_close, zawierającą zdalny deskryptor. Wynik wywołania powinien być taki, jak wynik wywołania close.

Powyższe wywołania powinny działać na procesie, który jest przez nas śledzony i jest akurat zatrzymany przez ptrace. W przypadku użycia na innym procesie, należy zwrócić ESRCH.

Zasady oceniania

Za zadanie można uzyskać do 10 punktów. Na ocenę zadania składają się dwie części:

  • wynik automatycznych testów (od 0 do 10 punktów)
  • ocena kodu rozwiązania (od 0 do -10 punktów)

Punktacja za kod

  1. -0.3 Arytmetyka na wskaźnikach bezpośrednio, zamiast container_of, offset_of, itp.
  2. -0.8 Pobieranie nadmiarowych referencji do plików (wyciek), np. jak podano MAP_ANONYMOUS oraz fd
  3. -0.1 Błędna obsługa MAP_FIXED na adres 0 (to jest dozwolona operacja o ile CONFIG_DEFAULT_MMAP_MIN_ADDR=0)
  4. -0.5 PTRACE_REMOTE_MMAP modyfikuje dodatkowo inne pola ptrace_remote_mmap niż addr (kopiuje z powrote do userspace te zmiany).
  5. -0.5 PTRACE_REMOTE_MREMAP nie zwraca nowego new_addr do userspace
  6. -0.5 Potencjalny wyścig przy obsłudze DUP_{FROM,TO}_REMOTE - samo fcheck() zamiast fget(), bez odpowiednio chroniącej blokady
  7. -1.0 Niepoprawny patch (nie nakłada się, brakuje ptrace_remote.h, nie kompiluje się, itp).

Forma rozwiązania

Jako rozwiązanie należy wysłać paczkę zawierającą:

  • patcha na jądro w wersji 4.9.13, w jednym z następujących formatów:
    • patch wygenerowaniy przez diffa z opcjami -uprN nakładający się przez patch -p1
    • git format-patch
  • krótki opis rozwiązania

Rozwiązania prosimy nadsyłać na adres marmarek@mimuw.edu.pl z kopią do mwk@mimuw.edu.pl.

Wskazówki

Można czerpać inspirację z systemu plików proc – on już zawiera mechanizmy dostępu do zdalnych procesów (choć tylko do odczytu).

Należy zwrócić uwagę na optymalizacje zawarte w kodzie do_mmap/do_munmap (i funkcji potomnych) zakładające, że kod ten jest wywoływany z procesu będącego wewnątrz danej przestrzeni adresowej. Należy poprawić te optymalizacje (np. wyłączając je dla procesów śledzonych przez ptrace), bądź zrealizować funkcjonalność przez wywołanie odpowiedniego kodu w kontekście docelowego procesu.