użytkownik musi zapewnić wystarczającą ilość miejsca dla wszystkich danych, które są kopiowane. Najlepiej powie ci, ile miejsca zapewnił, a ty sprawdzasz, czy wszystko pasuje.
Skopiowane dane (ogólnie) nie powinny zawierać żadnych wskaźników, ponieważ są "lokalne" dla innego "procesu" (jądro może być postrzegane jako oddzielny proces, jak to było, i jądro/interakcje użytkownika obejmują proces IPC procesu do procesu, podobny do wysyłania rzeczy przez lokalne lub nawet podłączone do Internetu gniazda).
Ponieważ jądro ma dość intymną wiedzę na temat procesu, można nieco ominąć te zasady, np. Można obliczyć wskaźnik użytkownika i skopiować kopię oryginalnych danych, odpowiednio zmodyfikować wskaźnik. Ale to marnotrawstwo. Możesz też skopiować wskaźnik jądra i po prostu nie używać go w kodzie użytkownika, ale teraz masz "wyciek danych", że "źli" mogą czasami wykorzystać na różne sposoby. W kwestii bezpieczeństwa-ludzie mówią, że opuściłeś szeroko otwarty "ukryty kanał".
W końcu, wtedy, „prawo” sposób to zrobić, wydaje się być coś takiego:
struct user_interface_version_of_struct {
int property;
int count;
int data[]; /* of size "count" */
};
Kod użytkownika malloc
s (lub inaczej układa się mieć wystarczającą ilość miejsca) na „interfejs użytkownika version "i wykonuje pewne systemowe wywołanie do jądra (read
,, rcvmsg
, ioctl
, cokolwiek, o ile wymaga to operacji typu" odczyt ") i mówi jądru:" tu jest pamięć przechowująca strukturę, a tutaj jest ile to jest "(w bajtach lub maksymalnej wartości count
, lub cokolwiek: użytkownik i jądro muszą po prostu uzgodnić protokół). Kod po stronie kernela następnie weryfikuje wartości użytkownika w odpowiedni sposób i albo kopiowanie jest najwygodniejsze, albo zwraca błąd.
„najwygodniejsze” jest czasami dwa oddzielne ops kopiowania lub niektóre put_user
połączeń, np, jeżeli strona jądro ma strukturę danych, który pokazał, można zrobić:
/* let's say ulen is the user supplied length in bytes,
and uaddr is the user-supplied address */
struct user_interface_version_of_struct *p;
needed = sizeof(*p) + 3 * sizeof(int);
if (needed > ulen)
return -ENOMEM; /* user did not supply enough space */
p = uaddr;
error = put_user(1024, &p->property);
if (error == 0)
error = put_user(3, &p->count);
if (error == 0 && copy_to_user(&p->data, localArray, 3 * sizeof(int))
error = -EFAULT;
Możesz mieć sytuację, w której musisz jednak dostosować się do niezbyt fajnego interfejsu.
Edit: jeśli dodając własne wywołanie systemowe (zamiast wiążąc się do read
lub ioctl
na przykład), można rozdzielić nagłówek i dane, jak w Adam Rosenfield's answer.
Mówisz więc, że nie mogę zadeklarować zmiennych w funkcji, a następnie "copy_to_user"? ** Muszę ** zaimportować je przed kopiowaniem? Miałem nadzieję, że proces kopiowania sprawił, że nie ma znaczenia, że pod koniec tej funkcji cała pamięć została zszokowana. –
@ niespodziewany62 Nie jestem w 100% pewny. Pozwól mi spojrzeć na to –
Bez problemu. Po prostu ciekawy, dlaczego potrzebne są dwa copy_to_users? Pomyślałem, że tablica była jedynie wskaźnikiem do pierwszego elementu w tablicy. Pytam, ponieważ nie jestem w stanie wykonać części 'destination-> array'. –