L'utente deve fornire abbastanza spazio per tutti i dati che vengono copiati. Idealmente ti dirà quanto spazio ha fornito e controllerai che tutto vada bene.
I dati copiati dovrebbero (in generale) non includere alcun puntatore, poiché sono "locali" a un diverso "processo" (il kernel può essere visualizzato come un processo separato, per così dire, e kernel/le interazioni dell'utente coinvolgono l'IPC processo-processo, simile all'invio di materiale tramite socket locali o addirittura connessi a Internet).
Poiché il kernel ha una conoscenza piuttosto approfondita di un processo, è possibile ignorare alcune regole, ad esempio, è possibile calcolare quale sarà il puntatore dell'utente e copiare una copia dei dati originali, con il puntatore modificato in modo appropriato. Ma è una specie di spreco. Oppure, puoi copiare un puntatore del kernel e non usarlo nel codice utente, ma ora stai "perdendo dati" che i "cattivi" a volte possono sfruttare in vari modi. Nella sicurezza, le persone parlano di aver lasciato un "canale segreto" aperto.
Alla fine, quindi, il modo "giusto" per fare questo tende ad essere qualcosa di simile:
struct user_interface_version_of_struct {
int property;
int count;
int data[]; /* of size "count" */
};
Il codice utente malloc
s (o altrimenti predispone la spazio sufficiente) il "interfaccia utente versione "e fa alcune chiamate di sistema al kernel (read
, receive
, rcvmsg
, ioctl
, qualunque cosa, purché implichi un'operazione di tipo" read ") e dice al kernel:" ecco la memoria che contiene la struct, ed ecco quanto è grande "(in byte, o il valore massimo count
, o qualsiasi altra cosa: utente e kernel devono semplicemente concordare il protocollo). Il codice sul lato kernel quindi verifica i valori dell'utente in un modo appropriato, e fa la copia-out comunque è più conveniente, o restituisce un errore.
"Il più conveniente" è a volte due ops separati copia, o alcuni put_user
chiamate, ad esempio, se il lato kernel ha la struttura dei dati che ha mostrato, si potrebbe fare:
/* 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;
Si può avere una situazione in cui devi comunque conformarti ad un'interfaccia non molto carina.
Edit: se si sta aggiungendo la propria chiamata di sistema (piuttosto che legare a read
o ioctl
per esempio), è possibile separare l'intestazione e di dati, come in Adam Rosenfield's answer.
Quindi, stai dicendo che non posso dichiarare le variabili localmente in una funzione e quindi "copy_to_user"? Ho ** bisogno di mallocli prima della copia? Speravo che il processo di copia lo facesse in modo che non importasse che alla fine della funzione tutta quella memoria fosse stata spazzata via. –
@ unexpected62 Non sono sicuro al 100%. Fammi esaminare questo –
Nessun problema. Solo curioso, perché sono necessari due copy_to_users? Pensavo che un array fosse solo il puntatore al primo elemento dell'array. Chiedo, perché non riesco a fare la parte 'destination-> array'. –