Người dùng cần phải cung cấp đủ không gian cho tất cả dữ liệu được sao chép. Lý tưởng nhất là anh ta sẽ cho bạn biết bao nhiêu không gian anh ta cung cấp, và bạn kiểm tra xem mọi thứ có phù hợp không.
Dữ liệu đã sao chép (nói chung) không bao gồm bất kỳ con trỏ nào, vì chúng là "cục bộ" với một "quy trình" khác nhau (hạt nhân có thể được xem như một quá trình riêng biệt, và hạt nhân/tương tác của người dùng liên quan đến quá trình xử lý IPC, tương tự như gửi nội dung qua ổ cắm được kết nối Internet cục bộ hoặc thậm chí cả Internet).
Vì hạt nhân có kiến thức khá thân mật về quy trình, bạn có thể tính toán các quy tắc này một chút, ví dụ: bạn có thể tính toán con trỏ của người dùng và sao chép bản sao dữ liệu ban đầu, với con trỏ được sửa đổi một cách thích hợp. Nhưng đó là loại lãng phí. Hoặc, bạn có thể sao chép một con trỏ hạt nhân và chỉ không sử dụng nó trong mã người dùng, nhưng bây giờ bạn đang "rò rỉ dữ liệu" mà "kẻ xấu" đôi khi có thể tận dụng theo nhiều cách khác nhau. Trong bảo mật-người-nói bạn đã để lại một "kênh bí mật" mở rộng.
Cuối cùng, sau đó, cách "đúng" để làm điều này có xu hướng được một cái gì đó như thế này:
struct user_interface_version_of_struct {
int property;
int count;
int data[]; /* of size "count" */
};
Mã dùng malloc
s (hoặc nếu không sắp xếp để có đủ không gian) các "giao diện người dùng phiên bản "và thực hiện một số cuộc gọi hệ thống tới hạt nhân (read
, receive
, rcvmsg
, ioctl
, bất cứ điều gì, miễn là nó liên quan đến hoạt động" đọc ") và nói với hạt nhân:" Đây là bộ nhớ giữ cấu trúc và đây là nó lớn đến mức nào "(tính theo byte, hoặc giá trị tối đa count
, hoặc bất cứ điều gì: người dùng và hạt nhân đơn giản chỉ cần đồng ý về giao thức). Mã phía nhân sau đó xác minh các giá trị của người dùng theo một cách thích hợp, và việc sao chép đó là thuận tiện nhất, hoặc trả về một lỗi.
"Hầu hết thuận tiện" là đôi khi hai ops riêng sao chép, hoặc một số put_user
cuộc gọi, ví dụ, nếu phía hạt nhân có cấu trúc dữ liệu mà bạn thấy, bạn có thể làm:
/* 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;
Bạn có thể có một tình huống mà bạn phải tuân theo một số giao diện không-rất-đẹp, mặc dù.
Edit: nếu bạn đang thêm cuộc gọi hệ thống của riêng bạn (chứ không phải buộc để read
hoặc ioctl
chẳng hạn), bạn có thể tách các header và dữ liệu, như trong Adam Rosenfield's answer.
Vì vậy, bạn đang nói rằng tôi không thể khai báo biến cục bộ trong một hàm và sau đó "copy_to_user" chúng? Tôi ** có ** để malloc chúng trước khi sao chép? Tôi đã hy vọng rằng quá trình sao chép đã làm cho nó để nó không quan trọng rằng ở phần cuối của chức năng tất cả những bộ nhớ đã bị thổi bay đi. –
@ unexpected62 Tôi không chắc chắn 100%. Hãy để tôi xem xét điều này –
Không sao cả. Chỉ cần tò mò, tại sao hai copy_to_users cần thiết? Tôi nghĩ rằng một mảng chỉ là con trỏ đến phần tử đầu tiên trong mảng. Tôi hỏi, bởi vì tôi không thể làm phần 'destination-> array'. –