Почему функция read () syscall блокирует передачу недопустимого указателя буфера?
Вот мой фрагмент кода read(STDIN, NULL, 10)
, выполненный в Linux-2.6.32.431. Я предполагал, что он вернется сразу же после просмотра исходного кода read () syscall:
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
И
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) //I suppose it should return here
return -EFAULT;
...
}
Однако он был заблокирован. После того, как я ввел несколько символов и нажал return, эта программа потребляла один символ и возвращала, в то время как остальные символы вводились в терминал.
Мой вопрос:
-
Почему был заблокирован вызов read ()?
-
Почему оставшиеся символы были введены в терминал.
2 ответа:
Я считаю, что
access_ok
не делает именно то, что подразумевает его название.Из комментариев в arch / x86 / include/asm / uaccess.h :
/** * access_ok: - Checks if a user space pointer is valid * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE. Note that * %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe * to write to a block, it is always safe to read from it. * @addr: User space pointer to start of block to check * @size: Size of block to check * * Context: User context only. This function may sleep. * * Checks if a pointer to a block of memory in user space is valid. * * Returns true (nonzero) if the memory block may be valid, false (zero) * if it is definitely invalid. * * Note that, depending on architecture, this function probably just * checks that the pointer is in the user space range - after calling * this function, memory access functions may still return -EFAULT. */
Комментарии кажутся точными; на x86, если вы проследите определение
access_ok
, вы найдете он просто проверяет (по существу), является лиaddr + size > user_addr_max()
. В частности, он возвращает "true" для нулевого указателя.Таким образом, вы должны проследить
vfs_read
немного дальше, в вызовfile->f_op->read()
, который предположительно вызываетread
функция для драйвера TTY, который предположительно находится там, где он блокирует.(обратите внимание, что POSIX ничего не гарантирует, когда вы передаете нулевой указатель на
read
, поэтому я бы не советовал этого делать.)[обновление]
Для вашего второго вопроса это та же причина, по которой эта последовательность считывает один символ, а затем передает остальные терминалу:
Все, что я сделал, это ввел "лалала" в команду$ head -c 1 > /dev/null lalala $ alala alala: command not found
head
. Ваша программа, вероятно, потребляя один символ входного телетайп , завершение работы (сбой), а затем остальная часть входных данных в TTY потребляется оболочкой после завершения работы программы.
Если вы проверяете
read
на странице руководства вы увидите, что:EFAULT
buf
находится вне вашего доступного адресного пространства.Указатель
NULL
все еще находится в доступном адресном пространстве всех процессов. Запись или разыменование указателяNULL
приводит к неопределенное поведение, но это все еще действительный адрес.Так что
read
вызов блокирует, потому что нет входных данных для чтения. Когда есть, процесс будет наиболее скорее всего, авария.