Czy da się zrobić
Olimpiadę Informatyczną
na BSD?
Michał Sidor
- student informatyki na MIMUWie
- sysadmin Olimpiady Informatycznej
- kontakt:
- twitter: @michcioperz
- irc: Michcioperz/Michciopierz
(pirc, freenode)
- xmpp: michcioperz@z.nom.pl
- twitter: @michcioperz
- nadal nie mam BSD
Czym jest Olimpiada?
- konkurs dla uczniów szkół średnich
- zadania algorytmiczne
- nacisk na złożoność asymptotyczną
- praktycznie wszyscy używają C++
Wejście
W pierwszym i jedynym wierszu standardowegowejścia znajdują się dwie liczby całkowite
a i b (0 ≤ a, b ≤ 10000).
Wyjście
Twój program powinien wypisać sumę liczb z wejścia, plus minus 1.
Sumżyce (sum) – 128 MB RAM
Weź i dodaj dwie liczby.
Wejście
Pierwszy wiersz standardowego wejścia zawiera dwie liczby całkowite n i k (1 ≤ k ≤ n \≤ 500000) oddzielone pojedynczym odstępem, oznaczające liczbę pracowników organizacji i maksymalną dopuszczalną liczbę zbuntowanych. Pracownicy są ponumerowani liczbami całkowitymi od 1 do n, przy czym szef ma numer 1. Kolejne n-1 wierszy opisuje strukturę organizacyjną: i-ty z tych wierszy
zawiera liczbę całkowitą p_i (p_i ≤ i) oznaczającą, że bezpośrednim przełożonym pracownika o numerze i+1 jest pracownik o numerze p_i.
Wyjście
Pierwszy i jedyny wiersz standardowego wyjścia powinien zawierać jedną liczbę rzeczywistą, oznaczającą szukane morale. Wyniki różniące się od prawidłowego o mniej niż 10^{−6} będą uznane zapoprawne.
[OI24] Sabotaż (sab) – 128 MB RAM
W pewnej organizacji, której nazwy nie możemy wymienić, relację przełożony-podwładny daje się przedstawić za pomocą drzewa — każdy pracownik, oprócz szefa, ma dokładnie jednego bezpośredniego przełożonego. Ponadto pracownikom nadawane są numery w kolejności, w jakiej są zatrudniani, a przełożony ma zawsze wcześniejszy numer od podwładnego.
Rada nadzorcza obawia się, że w szeregi organizacji mógł przeniknąć sabotażysta, chcący doprowadzić do buntu pracowników. Aby temu przeciwdziałać, rada jest zainteresowana utrzymywaniem wśród pracowników wysokiego morale (np. poprzez przyznawanie im dodatkowych premii, organizację festynów, czy zakup stołów do piłkarzyków). Morale wyraża się liczbą rzeczywistą x z zakresu od 0 do 1. Jeśli którykolwiek pracownik zauważy, że frakcja powyżej x spośród jego (bezpośrednich oraz pośrednich) podwładnych zbuntowała się, to sam dołączy do buntu i zmusi do tego wszystkich swoich podwładnych. Sabotażysta jest jednym z pracowników i w pewnym momencie ujawni się, buntując się jako pierwszy (ale nie zmusi do buntu swoich podwładnych).
Rada nadzorcza chce wiedzieć, jakie jest najmniejsze morale, które musi być utrzymane wśród pracowników, żeby potencjalny bunt mógł objąć co najwyżej k pracowników. Napisz program, który wyznaczy tę liczbę.
Czym jest SIO2?
- System Olimpiady Informatycznej (v2)
- Python, Django, PostgreSQL
- równoległe sprawdzanie wielu zadań
w kontrolowanym środowisku
w sposób deterministyczny -
https://github.com/sio2project/oioioi
oioioi sio2.mimuw.edu.pl
oioioi szkopul.edu.pl
sioworkersd
spr3g19
spr3g8
spr3g2
spr3g11
spr3g15
filetracker
sio2jail
time + ulimit + sandbox
- filtrowanie groźnych/zbędnych syscalli
- ograniczenie czasu
- ograniczenie pamięci
ograniczenie pamięci
Linux: setrlimit
#include <sys/resource.h>
struct rlimit lim;
lim.rlim_cur = memoryBytesLimit;
lim.rlim_max = memoryBytesLimit;
setrlimit(RLIMIT_AS, &lim);
FreeBSD: setrlimit
#include <sys/resource.h>
struct rlimit lim;
lim.rlim_cur = memoryBytesLimit;
lim.rlim_max = memoryBytesLimit;
setrlimit(RLIMIT_AS, &lim);
OpenBSD: setrlimit???
#define RLIMIT_AS RLIMIT_DATA /* ??? */
w sio2jailu to nie wszystko
- limit trochę większy niż zadeklarowany
- przechwytywanie syscalli mmap
- na każdym evencie sprawdzenie vm_peak
w /proc/$pid/status - wszystko po to, żeby odróżnić zwykły SIGSEGV
od przekroczenia limitu pamięci
FreeBSD: dtrace (???)
- probować syscalle alokujące pamięć
- probować instrukcje przesuwające stack pointer
- czy to ma sens?
ograniczenie czasu
zliczanie instrukcji przy użyciu
liczników sprzętowych
Linux: perf_event_open
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
// opakuj:
struct perf_event_attr attrs;
memset(&attrs, 0, sizeof(attrs));
attrs.type = PERF_TYPE_HARDWARE;
attrs.size = sizeof(attrs);
attrs.config = PERF_COUNT_HW_INSTRUCTIONS;
attrs.exclude_user = 0;
attrs.exclude_kernel = 1;
attrs.exclude_hv = 1;
attrs.disabled = 1;
attrs.enable_on_exec = 1;
attrs.sample_period = instructionsLimit;
attrs.wakeup_events = 1;
fd = syscall(__NR_perf_event_open, attrs,
p, -1, -1, PERF_FLAG_FD_NO_GROUP);
Linux: dostaliśmy fajne fd
fcntl(fd, F_SETOWN, getpid());
int oldFlags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, oldFlags | O_ASYNC);
void sigioHandler(int sig) {}
sigaction(SIGIO, sioioHandler);
// reaguj:
for (;;) {
int res = waitpid(p);
long long int instrs;
read(fd, &instrs, sizeof(instrs));
if (instrs > instructionsLimit && res != p) {
kill(p, SIGKILL);
return 1;
} else if (res == p) {
return 0;
}
}
openbsd: pctr(4)
czy jest na sali użytkownik?
FreeBSD: hwpmc(4), pmc(3)
#include <pmc.h>
#include <pmclog.h>
int tun[2];
pipe(tun);
pmc_id_t id;
pmc_allocate("instructions", PMC_MODE_TC,
PMC_F_LOG_PROCSW | PMC_F_LOG_PROCEXIT,
PMC_CPU_ANY, &id, -1);
pmc_configure_logfile(tun[1]);
pmc_attach(id, p);
pmc_start(id);
FreeBSD: pmclog(3)
void* reader = pmclog_open(tun[0]);
struct pmclog_ev ev;
uint64_t instrs = 0;
for (;;) {
pmclog_read(reader, ev);
switch (ev.pl_type) {
case PMCLOG_TYPE_PROCCSW:
instrs += ev.pl_u.pl_c.pl_value;
break;
case PMCLOG_TYPE_PROCEXIT:
instrs += ev.pl_u.pl_e.pl_value;
}
if (instrs > instructions_limit) {
kill(p, SIGKILL);
return 1;
}
}
struct pmclog_ev {
enum pmclog_state pl_state; /* parser state */
off_t pl_offset; /* byte offset in stream */
size_t pl_count; /* count of records so far */
struct timespec pl_ts; /* log entry timestamp */
enum pmclog_type pl_type; /* log entry kind */
union { /* log entry data */
struct pmclog_ev_callchain pl_cc;
struct pmclog_ev_closelog pl_cl;
struct pmclog_ev_dropnotify pl_d;
struct pmclog_ev_initialize pl_i;
struct pmclog_ev_map_in pl_mi;
struct pmclog_ev_map_out pl_mo;
struct pmclog_ev_pmcallocate pl_a;
struct pmclog_ev_pmcallocatedyn pl_ad;
struct pmclog_ev_pmcattach pl_t;
struct pmclog_ev_pmcdetach pl_d;
struct pmclog_ev_proccsw pl_c;
struct pmclog_ev_procexec pl_x;
struct pmclog_ev_procexit pl_e;
struct pmclog_ev_procfork pl_f;
struct pmclog_ev_sysexit pl_e;
struct pmclog_ev_userdata pl_u;
} pl_u;
};
FreeBSD na szybko: pmcstat(8)
# kldload hwpmc
$ pmcstat -p instructions ./1-sec-prog
FreeBSD na szybko: dtrace
$ cat instructor.d
dtrace:::BEGIN
{
i = 0;
}
pid$target:::
{
i = i + 1;
}
dtrace:::END
{
trace(i);
}
$ dtrace -s instructor.d -c ./1-sec-prog
"na szybko":
- program ma 2*10^9 instrukcji
- ma trwać sekundę na Pentiumie 4
- pod dtracem wykonuje się 10^6 na sekundę
filtrowanie syscalli
strict mode
// Linux
seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL);
// FreeBSD
cap_enter();
// OpenBSD
pledge("", NULL);
dlaczego strict mode nie pomaga?
- Linux: wyjście z programu w C++ wykonuje
zamiast syscalla exit zabroniony syscall exit_group - FreeBSD: proces w capability mode nadal może
np. wyprodukować fork bombę - tylko OpenBSD ma prosty mechanizm do
nałożenia restrykcji dopiero po execv
ptrace
#include <sys/ptrace.h>
#include <linux/user.h>
// dziecko zgłasza się
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
// rodzic po złapaniu sygnału odczytuje rejestr
eax = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
switch (eax) {
case SYS_write:
case SYS_read:
/* ... */
break;
default:
ptrace(PTRACE_KILL, child, NULL, NULL);
return 1;
}
ptrace(PTRACE_SYSEMU, child, NULL, NULL);
sio2jail używa zaawansowanych filtrów seccompa
używając sporej ilości C++'a
FreeBSD: dtrace (szkic)
syscall::fork:entry,
syscall::open:entry,
...
/pid == $target/
{
exit(1);
}
Czy da się?
sio2bsd
By Michał Sidor
sio2bsd
- 1,925