mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:57:35 +00:00
Kernel: Make copy_to/from_user safe and remove unnecessary checks
Since the CPU already does almost all necessary validation steps for us, we don't really need to attempt to do this. Doing it ourselves doesn't really work very reliably, because we'd have to account for other processors modifying virtual memory, and we'd have to account for e.g. pages not being able to be allocated due to insufficient resources. So change the copy_to/from_user (and associated helper functions) to use the new safe_memcpy, which will return whether it succeeded or not. The only manual validation step needed (which the CPU can't perform for us) is making sure the pointers provided by user mode aren't pointing to kernel mappings. To make it easier to read/write from/to either kernel or user mode data add the UserOrKernelBuffer helper class, which will internally either use copy_from/to_user or directly memcpy, or pass the data through directly using a temporary buffer on the stack. Last but not least we need to keep syscall params trivial as we need to copy them from/to user mode using copy_from/to_user.
This commit is contained in:
parent
7d1b8417bd
commit
c8d9f1b9c9
149 changed files with 1585 additions and 1244 deletions
|
@ -60,14 +60,14 @@ String MasterPTY::pts_name() const
|
|||
return m_pts_name;
|
||||
}
|
||||
|
||||
KResultOr<size_t> MasterPTY::read(FileDescription&, size_t, u8* buffer, size_t size)
|
||||
KResultOr<size_t> MasterPTY::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
if (!m_slave && m_buffer.is_empty())
|
||||
return 0;
|
||||
return m_buffer.read(buffer, size);
|
||||
}
|
||||
|
||||
KResultOr<size_t> MasterPTY::write(FileDescription&, size_t, const u8* buffer, size_t size)
|
||||
KResultOr<size_t> MasterPTY::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
if (!m_slave)
|
||||
return KResult(-EIO);
|
||||
|
@ -98,12 +98,11 @@ void MasterPTY::notify_slave_closed(Badge<SlavePTY>)
|
|||
m_slave = nullptr;
|
||||
}
|
||||
|
||||
ssize_t MasterPTY::on_slave_write(const u8* data, ssize_t size)
|
||||
ssize_t MasterPTY::on_slave_write(const UserOrKernelBuffer& data, ssize_t size)
|
||||
{
|
||||
if (m_closed)
|
||||
return -EIO;
|
||||
m_buffer.write(data, size);
|
||||
return size;
|
||||
return m_buffer.write(data, size);
|
||||
}
|
||||
|
||||
bool MasterPTY::can_write_from_slave() const
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
|
||||
unsigned index() const { return m_index; }
|
||||
String pts_name() const;
|
||||
ssize_t on_slave_write(const u8*, ssize_t);
|
||||
ssize_t on_slave_write(const UserOrKernelBuffer&, ssize_t);
|
||||
bool can_write_from_slave() const;
|
||||
void notify_slave_closed(Badge<SlavePTY>);
|
||||
bool is_closed() const { return m_closed; }
|
||||
|
@ -50,8 +50,8 @@ public:
|
|||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override;
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override;
|
||||
virtual bool can_read(const FileDescription&, size_t) const override;
|
||||
virtual bool can_write(const FileDescription&, size_t) const override;
|
||||
virtual KResult close() override;
|
||||
|
|
|
@ -48,8 +48,8 @@ public:
|
|||
|
||||
// ^CharacterDevice
|
||||
virtual KResultOr<NonnullRefPtr<FileDescription>> open(int options) override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override { return 0; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override { return 0; }
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return 0; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return 0; }
|
||||
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
|
||||
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||
|
||||
|
|
|
@ -62,17 +62,22 @@ String SlavePTY::tty_name() const
|
|||
void SlavePTY::echo(u8 ch)
|
||||
{
|
||||
if (should_echo_input()) {
|
||||
m_master->on_slave_write(&ch, 1);
|
||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(&ch);
|
||||
m_master->on_slave_write(buffer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SlavePTY::on_master_write(const u8* buffer, ssize_t size)
|
||||
void SlavePTY::on_master_write(const UserOrKernelBuffer& buffer, ssize_t size)
|
||||
{
|
||||
for (ssize_t i = 0; i < size; ++i)
|
||||
emit(buffer[i]);
|
||||
ssize_t nread = buffer.read_buffered<128>(size, [&](const u8* data, size_t data_size) {
|
||||
for (size_t i = 0; i < data_size; ++i)
|
||||
emit(data[i]);
|
||||
return (ssize_t)data_size;
|
||||
});
|
||||
(void)nread;
|
||||
}
|
||||
|
||||
ssize_t SlavePTY::on_tty_write(const u8* data, ssize_t size)
|
||||
ssize_t SlavePTY::on_tty_write(const UserOrKernelBuffer& data, ssize_t size)
|
||||
{
|
||||
m_time_of_last_write = kgettimeofday().tv_sec;
|
||||
return m_master->on_slave_write(data, size);
|
||||
|
@ -90,7 +95,7 @@ bool SlavePTY::can_read(const FileDescription& description, size_t offset) const
|
|||
return TTY::can_read(description, offset);
|
||||
}
|
||||
|
||||
KResultOr<size_t> SlavePTY::read(FileDescription& description, size_t offset, u8* buffer, size_t size)
|
||||
KResultOr<size_t> SlavePTY::read(FileDescription& description, size_t offset, UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
if (m_master->is_closed())
|
||||
return 0;
|
||||
|
|
|
@ -37,7 +37,7 @@ class SlavePTY final : public TTY {
|
|||
public:
|
||||
virtual ~SlavePTY() override;
|
||||
|
||||
void on_master_write(const u8*, ssize_t);
|
||||
void on_master_write(const UserOrKernelBuffer&, ssize_t);
|
||||
unsigned index() const { return m_index; }
|
||||
|
||||
time_t time_of_last_write() const { return m_time_of_last_write; }
|
||||
|
@ -45,12 +45,12 @@ public:
|
|||
private:
|
||||
// ^TTY
|
||||
virtual String tty_name() const override;
|
||||
virtual ssize_t on_tty_write(const u8*, ssize_t) override;
|
||||
virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) override;
|
||||
virtual void echo(u8) override;
|
||||
|
||||
// ^CharacterDevice
|
||||
virtual bool can_read(const FileDescription&, size_t) const override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
|
||||
virtual bool can_write(const FileDescription&, size_t) const override;
|
||||
virtual const char* class_name() const override { return "SlavePTY"; }
|
||||
virtual KResult close() override;
|
||||
|
|
|
@ -52,7 +52,7 @@ void TTY::set_default_termios()
|
|||
memcpy(m_termios.c_cc, default_cc, sizeof(default_cc));
|
||||
}
|
||||
|
||||
KResultOr<size_t> TTY::read(FileDescription&, size_t, u8* buffer, size_t size)
|
||||
KResultOr<size_t> TTY::read(FileDescription&, size_t, UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
if (Process::current()->pgid() != pgid()) {
|
||||
// FIXME: Should we propigate this error path somehow?
|
||||
|
@ -63,33 +63,40 @@ KResultOr<size_t> TTY::read(FileDescription&, size_t, u8* buffer, size_t size)
|
|||
if (m_input_buffer.size() < static_cast<size_t>(size))
|
||||
size = m_input_buffer.size();
|
||||
|
||||
ssize_t nwritten;
|
||||
if (in_canonical_mode()) {
|
||||
size_t i = 0;
|
||||
for (; i < size; i++) {
|
||||
u8 ch = m_input_buffer.dequeue();
|
||||
if (ch == '\0') {
|
||||
//Here we handle a ^D line, so we don't add the
|
||||
//character to the output.
|
||||
m_available_lines--;
|
||||
break;
|
||||
} else if (ch == '\n' || is_eol(ch)) {
|
||||
buffer[i] = ch;
|
||||
i++;
|
||||
m_available_lines--;
|
||||
break;
|
||||
nwritten = buffer.write_buffered<512>(size, [&](u8* data, size_t data_size) {
|
||||
size_t i = 0;
|
||||
for (; i < data_size; i++) {
|
||||
u8 ch = m_input_buffer.dequeue();
|
||||
if (ch == '\0') {
|
||||
//Here we handle a ^D line, so we don't add the
|
||||
//character to the output.
|
||||
m_available_lines--;
|
||||
break;
|
||||
} else if (ch == '\n' || is_eol(ch)) {
|
||||
data[i] = ch;
|
||||
i++;
|
||||
m_available_lines--;
|
||||
break;
|
||||
}
|
||||
data[i] = ch;
|
||||
}
|
||||
buffer[i] = ch;
|
||||
}
|
||||
return i;
|
||||
return (ssize_t)i;
|
||||
});
|
||||
} else {
|
||||
nwritten = buffer.write_buffered<512>(size, [&](u8* data, size_t data_size) {
|
||||
for (size_t i = 0; i < data_size; i++)
|
||||
data[i] = m_input_buffer.dequeue();
|
||||
return (ssize_t)data_size;
|
||||
});
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; i++)
|
||||
buffer[i] = m_input_buffer.dequeue();
|
||||
|
||||
return size;
|
||||
if (nwritten < 0)
|
||||
return KResult(nwritten);
|
||||
return (size_t)nwritten;
|
||||
}
|
||||
|
||||
KResultOr<size_t> TTY::write(FileDescription&, size_t, const u8* buffer, size_t size)
|
||||
KResultOr<size_t> TTY::write(FileDescription&, size_t, const UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
if (Process::current()->pgid() != pgid()) {
|
||||
(void)Process::current()->send_signal(SIGTTOU, nullptr);
|
||||
|
@ -337,19 +344,17 @@ int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
|||
}
|
||||
case TCGETS: {
|
||||
user_termios = reinterpret_cast<termios*>(arg);
|
||||
if (!current_process.validate_write(user_termios, sizeof(termios)))
|
||||
if (!copy_to_user(user_termios, &m_termios))
|
||||
return -EFAULT;
|
||||
copy_to_user(user_termios, &m_termios);
|
||||
return 0;
|
||||
}
|
||||
case TCSETS:
|
||||
case TCSETSF:
|
||||
case TCSETSW: {
|
||||
user_termios = reinterpret_cast<termios*>(arg);
|
||||
if (!current_process.validate_read(user_termios, sizeof(termios)))
|
||||
return -EFAULT;
|
||||
termios termios;
|
||||
copy_from_user(&termios, user_termios);
|
||||
if (!copy_from_user(&termios, user_termios))
|
||||
return -EFAULT;
|
||||
set_termios(termios);
|
||||
if (request == TCSETSF)
|
||||
flush_input();
|
||||
|
@ -365,21 +370,19 @@ int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
|||
return 0;
|
||||
case TIOCGWINSZ:
|
||||
user_winsize = reinterpret_cast<winsize*>(arg);
|
||||
if (!current_process.validate_write(user_winsize, sizeof(winsize)))
|
||||
return -EFAULT;
|
||||
winsize ws;
|
||||
ws.ws_row = m_rows;
|
||||
ws.ws_col = m_columns;
|
||||
ws.ws_xpixel = 0;
|
||||
ws.ws_ypixel = 0;
|
||||
copy_to_user(user_winsize, &ws);
|
||||
if (!copy_to_user(user_winsize, &ws))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case TIOCSWINSZ: {
|
||||
user_winsize = reinterpret_cast<winsize*>(arg);
|
||||
if (!current_process.validate_read(user_winsize, sizeof(winsize)))
|
||||
return -EFAULT;
|
||||
winsize ws;
|
||||
copy_from_user(&ws, user_winsize);
|
||||
if (!copy_from_user(&ws, user_winsize))
|
||||
return -EFAULT;
|
||||
if (ws.ws_col == m_columns && ws.ws_row == m_rows)
|
||||
return 0;
|
||||
m_rows = ws.ws_row;
|
||||
|
|
|
@ -39,8 +39,8 @@ class TTY : public CharacterDevice {
|
|||
public:
|
||||
virtual ~TTY() override;
|
||||
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, u8*, size_t) override;
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const u8*, size_t) override;
|
||||
virtual KResultOr<size_t> read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override;
|
||||
virtual KResultOr<size_t> write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override;
|
||||
virtual bool can_read(const FileDescription&, size_t) const override;
|
||||
virtual bool can_write(const FileDescription&, size_t) const override;
|
||||
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override final;
|
||||
|
@ -63,7 +63,7 @@ public:
|
|||
void hang_up();
|
||||
|
||||
protected:
|
||||
virtual ssize_t on_tty_write(const u8*, ssize_t) = 0;
|
||||
virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) = 0;
|
||||
void set_size(unsigned short columns, unsigned short rows);
|
||||
|
||||
TTY(unsigned major, unsigned minor);
|
||||
|
|
|
@ -241,14 +241,17 @@ void VirtualConsole::on_key_pressed(KeyboardDevice::Event event)
|
|||
m_terminal.handle_key_press(event.key, event.code_point, event.flags);
|
||||
}
|
||||
|
||||
ssize_t VirtualConsole::on_tty_write(const u8* data, ssize_t size)
|
||||
ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t size)
|
||||
{
|
||||
ScopedSpinLock lock(s_lock);
|
||||
for (ssize_t i = 0; i < size; ++i)
|
||||
m_terminal.on_input(data[i]);
|
||||
ssize_t nread = data.read_buffered<512>((size_t)size, [&](const u8* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; ++i)
|
||||
m_terminal.on_input(buffer[i]);
|
||||
return (ssize_t)buffer_bytes;
|
||||
});
|
||||
if (m_active)
|
||||
flush_dirty_lines();
|
||||
return size;
|
||||
return nread;
|
||||
}
|
||||
|
||||
void VirtualConsole::set_vga_start_row(u16 row)
|
||||
|
@ -336,7 +339,8 @@ void VirtualConsole::emit(const u8* data, size_t size)
|
|||
void VirtualConsole::echo(u8 ch)
|
||||
{
|
||||
if (should_echo_input()) {
|
||||
on_tty_write(&ch, 1);
|
||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(&ch);
|
||||
on_tty_write(buffer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ private:
|
|||
virtual void on_key_pressed(KeyboardDevice::Event) override;
|
||||
|
||||
// ^TTY
|
||||
virtual ssize_t on_tty_write(const u8*, ssize_t) override;
|
||||
virtual ssize_t on_tty_write(const UserOrKernelBuffer&, ssize_t) override;
|
||||
virtual String tty_name() const override { return m_tty_name; }
|
||||
virtual void echo(u8) override;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue