diff --git a/headers/private/kernel/compat/fcntl_compat.h b/headers/private/kernel/compat/fcntl_compat.h new file mode 100644 index 0000000000..25b38e79de --- /dev/null +++ b/headers/private/kernel/compat/fcntl_compat.h @@ -0,0 +1,74 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_FCNTL_H +#define _KERNEL_COMPAT_FCNTL_H + + +#include + + +struct compat_flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +} _PACKED; + + +static_assert(sizeof(struct compat_flock) == 24, + "size of compat_flock mismatch"); + + +inline status_t +copy_ref_var_from_user(struct flock* userFlock, struct flock &flock) +{ + if (!IS_USER_ADDRESS(userFlock)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_flock compat_flock; + if (user_memcpy(&compat_flock, userFlock, sizeof(compat_flock)) < B_OK) + return B_BAD_ADDRESS; + flock.l_type = compat_flock.l_type; + flock.l_whence = compat_flock.l_whence; + flock.l_start = compat_flock.l_start; + flock.l_len = compat_flock.l_len; + flock.l_pid = compat_flock.l_pid; + } else { + if (user_memcpy(&flock, userFlock, sizeof(struct flock)) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_to_user(struct flock &flock, struct flock* userFlock) +{ + if (!IS_USER_ADDRESS(userFlock)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_flock compat_flock; + compat_flock.l_type = flock.l_type; + compat_flock.l_whence = flock.l_whence; + compat_flock.l_start = flock.l_start; + compat_flock.l_len = flock.l_len; + compat_flock.l_pid = flock.l_pid; + if (user_memcpy(userFlock, &compat_flock, sizeof(compat_flock)) < B_OK) + return B_BAD_ADDRESS; + } else { + if (user_memcpy(userFlock, &flock, sizeof(flock)) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +#endif // _KERNEL_COMPAT_FCNTL_H diff --git a/headers/private/kernel/compat/fs_attr_compat.h b/headers/private/kernel/compat/fs_attr_compat.h new file mode 100644 index 0000000000..b657fb9c2e --- /dev/null +++ b/headers/private/kernel/compat/fs_attr_compat.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_FS_ATTR_H +#define _KERNEL_COMPAT_FS_ATTR_H + + +#include + + +struct compat_attr_info { + uint32 type; + off_t size; +} _PACKED; + + +static_assert(sizeof(compat_attr_info) == 12, + "size of compat_attr_info mismatch"); + + +inline status_t +copy_ref_var_to_user(attr_info &info, attr_info* userInfo) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_attr_info compat_info; + compat_info.type = info.type; + compat_info.size = info.size; + if (user_memcpy(userInfo, &compat_info, sizeof(compat_info)) < B_OK) + return B_BAD_ADDRESS; + } else { + if (user_memcpy(userInfo, &info, sizeof(info)) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +#endif // _KERNEL_COMPAT_FS_ATTR_H diff --git a/headers/private/kernel/compat/fs_info_compat.h b/headers/private/kernel/compat/fs_info_compat.h new file mode 100644 index 0000000000..67934bcb29 --- /dev/null +++ b/headers/private/kernel/compat/fs_info_compat.h @@ -0,0 +1,94 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_FS_INFO_H +#define _KERNEL_COMPAT_FS_INFO_H + + +#include + + +typedef struct compat_fs_info { + dev_t dev; /* volume dev_t */ + ino_t root; /* root ino_t */ + uint32 flags; /* flags (see above) */ + off_t block_size; /* fundamental block size */ + off_t io_size; /* optimal i/o size */ + off_t total_blocks; /* total number of blocks */ + off_t free_blocks; /* number of free blocks */ + off_t total_nodes; /* total number of nodes */ + off_t free_nodes; /* number of free nodes */ + char device_name[128]; /* device holding fs */ + char volume_name[B_FILE_NAME_LENGTH]; /* volume name */ + char fsh_name[B_OS_NAME_LENGTH]; /* name of fs handler */ +} _PACKED compat_fs_info; + + +static_assert(sizeof(compat_fs_info) == 480, + "size of compat_fs_info mismatch"); + + +inline status_t +copy_ref_var_to_user(fs_info &info, fs_info* userInfo) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_fs_info compat_info; + compat_info.dev = info.dev; + compat_info.root = info.root; + compat_info.flags = info.flags; + compat_info.block_size = info.block_size; + compat_info.io_size = info.io_size; + compat_info.total_blocks = info.total_blocks; + compat_info.free_blocks = info.free_blocks; + compat_info.total_nodes = info.total_nodes; + compat_info.free_nodes = info.free_nodes; + strlcpy(compat_info.device_name, info.device_name, 128); + strlcpy(compat_info.volume_name, info.volume_name, B_FILE_NAME_LENGTH); + strlcpy(compat_info.fsh_name, info.fsh_name, B_OS_NAME_LENGTH); + if (user_memcpy(userInfo, &compat_info, sizeof(compat_info)) < B_OK) + return B_BAD_ADDRESS; + } else { + if (user_memcpy(userInfo, &info, sizeof(info)) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_from_user(fs_info* userInfo, fs_info &info) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + compat_fs_info compat_info; + if (user_memcpy(&compat_info, userInfo, sizeof(compat_info)) < B_OK) + return B_BAD_ADDRESS; + info.dev = compat_info.dev; + info.root = compat_info.root; + info.flags = compat_info.flags; + info.block_size = compat_info.block_size; + info.io_size = compat_info.io_size; + info.total_blocks = compat_info.total_blocks; + info.free_blocks = compat_info.free_blocks; + info.total_nodes = compat_info.total_nodes; + info.free_nodes = compat_info.free_nodes; + strlcpy(info.device_name, compat_info.device_name, 128); + strlcpy(info.volume_name, compat_info.volume_name, B_FILE_NAME_LENGTH); + strlcpy(info.fsh_name, compat_info.fsh_name, B_OS_NAME_LENGTH); + } else if (user_memcpy(&info, userInfo, sizeof(info)) < B_OK) { + return B_BAD_ADDRESS; + } + return B_OK; +} + + +#endif // _KERNEL_COMPAT_FS_INFO_H diff --git a/headers/private/kernel/compat/stat_compat.h b/headers/private/kernel/compat/stat_compat.h new file mode 100644 index 0000000000..08f487a676 --- /dev/null +++ b/headers/private/kernel/compat/stat_compat.h @@ -0,0 +1,117 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_STAT_H +#define _KERNEL_COMPAT_STAT_H + + +#include +#include + + +struct compat_stat { + dev_t st_dev; /* device ID that this file resides on */ + ino_t st_ino; /* this file's serial inode ID */ + mode_t st_mode; /* file mode (rwx for user, group, etc) */ + nlink_t st_nlink; /* number of hard links to this file */ + uid_t st_uid; /* user ID of the owner of this file */ + gid_t st_gid; /* group ID of the owner of this file */ + off_t st_size; /* size in bytes of this file */ + dev_t st_rdev; /* device type (not used) */ + blksize_t st_blksize; /* preferred block size for I/O */ + struct compat_timespec st_atim; /* last access time */ + struct compat_timespec st_mtim; /* last modification time */ + struct compat_timespec st_ctim; /* last change time, not creation time */ + struct compat_timespec st_crtim; /* creation time */ + __haiku_uint32 st_type; /* attribute/index type */ + blkcnt_t st_blocks; /* number of blocks allocated for object */ +} _PACKED; + + +static_assert(sizeof(struct compat_stat) == 88, + "size of struct compat_stat mismatch"); + + +inline status_t +copy_ref_var_to_user(struct stat &stat, struct stat* userStat, size_t size) +{ + if (!IS_USER_ADDRESS(userStat)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + if (size > sizeof(compat_stat)) + return B_BAD_VALUE; + struct compat_stat compat_stat; + compat_stat.st_dev = stat.st_dev; + compat_stat.st_ino = stat.st_ino; + compat_stat.st_mode = stat.st_mode; + compat_stat.st_nlink = stat.st_nlink; + compat_stat.st_uid = stat.st_gid; + compat_stat.st_size = stat.st_size; + compat_stat.st_rdev = stat.st_rdev; + compat_stat.st_blksize = stat.st_blksize; + compat_stat.st_atim.tv_sec = stat.st_atim.tv_sec; + compat_stat.st_atim.tv_nsec = stat.st_atim.tv_nsec; + compat_stat.st_mtim.tv_sec = stat.st_mtim.tv_sec; + compat_stat.st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + compat_stat.st_ctim.tv_sec = stat.st_ctim.tv_sec; + compat_stat.st_ctim.tv_nsec = stat.st_ctim.tv_nsec; + compat_stat.st_crtim.tv_sec = stat.st_crtim.tv_sec; + compat_stat.st_crtim.tv_nsec = stat.st_crtim.tv_nsec; + compat_stat.st_type = stat.st_type; + compat_stat.st_blocks = stat.st_blocks; + if (user_memcpy(userStat, &compat_stat, size) < B_OK) + return B_BAD_ADDRESS; + } else { + if (size > sizeof(struct stat)) + return B_BAD_VALUE; + + if (user_memcpy(userStat, &stat, size) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_to_user(struct stat &stat, struct stat* userStat) +{ + if (!IS_USER_ADDRESS(userStat)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + struct compat_stat compat_stat; + compat_stat.st_dev = stat.st_dev; + compat_stat.st_ino = stat.st_ino; + compat_stat.st_mode = stat.st_mode; + compat_stat.st_nlink = stat.st_nlink; + compat_stat.st_uid = stat.st_gid; + compat_stat.st_size = stat.st_size; + compat_stat.st_rdev = stat.st_rdev; + compat_stat.st_blksize = stat.st_blksize; + compat_stat.st_atim.tv_sec = stat.st_atim.tv_sec; + compat_stat.st_atim.tv_nsec = stat.st_atim.tv_nsec; + compat_stat.st_mtim.tv_sec = stat.st_mtim.tv_sec; + compat_stat.st_mtim.tv_nsec = stat.st_mtim.tv_nsec; + compat_stat.st_ctim.tv_sec = stat.st_ctim.tv_sec; + compat_stat.st_ctim.tv_nsec = stat.st_ctim.tv_nsec; + compat_stat.st_crtim.tv_sec = stat.st_crtim.tv_sec; + compat_stat.st_crtim.tv_nsec = stat.st_crtim.tv_nsec; + compat_stat.st_type = stat.st_type; + compat_stat.st_blocks = stat.st_blocks; + if (user_memcpy(userStat, &compat_stat, sizeof(compat_stat)) < B_OK) + return B_BAD_ADDRESS; + } else { + if (user_memcpy(userStat, &stat, sizeof(stat)) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + + +#endif // _KERNEL_COMPAT_STAT_H diff --git a/headers/private/kernel/compat/time_compat.h b/headers/private/kernel/compat/time_compat.h new file mode 100644 index 0000000000..819a79d074 --- /dev/null +++ b/headers/private/kernel/compat/time_compat.h @@ -0,0 +1,19 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_TIME_H +#define _KERNEL_COMPAT_TIME_H + + +#include + + +struct compat_timespec { + uint32 tv_sec; /* seconds */ + uint32 tv_nsec; /* and nanoseconds */ +}; + + +#endif // _KERNEL_COMPAT_TIME_H diff --git a/headers/private/kernel/compat/vfs_defs_compat.h b/headers/private/kernel/compat/vfs_defs_compat.h new file mode 100644 index 0000000000..05a79ba1cd --- /dev/null +++ b/headers/private/kernel/compat/vfs_defs_compat.h @@ -0,0 +1,79 @@ +/* + * Copyright 2018, Haiku Inc. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_COMPAT_VFS_DEFS_H +#define _KERNEL_COMPAT_VFS_DEFS_H + + +#include + + +struct compat_fd_info { + int number; + int32 open_mode; + dev_t device; + ino_t node; +} _PACKED; + + +static_assert(sizeof(compat_fd_info) == 20, + "size of compat_fd_info mismatch"); + + +inline status_t +copy_ref_var_to_user(attr_info &info, attr_info* userInfo, size_t size) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + if (size != sizeof(compat_fd_info)) + return B_BAD_VALUE; + compat_fd_info compat_info; + compat_info.number = info.number; + compat_info.open_mode = info.open_mode; + compat_info.device = info.device; + compat_info.node = info.node; + if (user_memcpy(userInfo, &compat_info, size) < B_OK) + return B_BAD_ADDRESS; + } else { + if (size != sizeof(fd_info)) + return B_BAD_VALUE; + if (user_memcpy(userInfo, &info, size) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +inline status_t +copy_ref_var_from_user(fd_info* userInfo, fd_info &info, size_t size) +{ + if (!IS_USER_ADDRESS(userInfo)) + return B_BAD_ADDRESS; + Thread* thread = thread_get_current_thread(); + bool compatMode = (thread->flags & THREAD_FLAGS_COMPAT_MODE) != 0; + if (compatMode) { + if (size != sizeof(compat_fd_info)) + return B_BAD_VALUE; + compat_fd_info compat_info; + if (user_memcpy(&compat_info, userInfo, size) < B_OK) + return B_BAD_ADDRESS; + info.number = compat_info.number; + info.open_mode = compat_info.open_mode; + info.device = compat_info.device; + info.node = compat_info.node; + } else { + if (size != sizeof(fd_info)) + return B_BAD_VALUE; + if (user_memcpy(&info, userInfo, size) < B_OK) + return B_BAD_ADDRESS; + } + return B_OK; +} + + +#endif // _KERNEL_COMPAT_VFS_DEFS_H diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 4f1657a55c..192ecd1210 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -49,11 +49,19 @@ #include #include #include +#include #include #include #include #include +#ifdef _COMPAT_MODE +# include +# include +# include +# include +#endif + #include "EntryCache.h" #include "fifo.h" #include "IORequest.h" @@ -6116,10 +6124,8 @@ common_fcntl(int fd, int op, size_t argument, bool kernel) if (op == F_SETLK || op == F_SETLKW || op == F_GETLK) { if (descriptor->type != FDTYPE_FILE) status = B_BAD_VALUE; - else if (user_memcpy(&flock, (struct flock*)argument, - sizeof(struct flock)) != B_OK) - status = B_BAD_ADDRESS; - + else + status = copy_ref_var_from_user((struct flock*)argument, flock); if (status != B_OK) { put_fd(descriptor); return status; @@ -8798,10 +8804,7 @@ _user_read_fs_info(dev_t device, struct fs_info* userInfo) if (status != B_OK) return status; - if (user_memcpy(userInfo, &info, sizeof(struct fs_info)) != B_OK) - return B_BAD_ADDRESS; - - return B_OK; + return copy_ref_var_to_user(info, userInfo); } @@ -8813,9 +8816,9 @@ _user_write_fs_info(dev_t device, const struct fs_info* userInfo, int mask) if (userInfo == NULL) return B_BAD_VALUE; - if (!IS_USER_ADDRESS(userInfo) - || user_memcpy(&info, userInfo, sizeof(struct fs_info)) != B_OK) - return B_BAD_ADDRESS; + status_t status = copy_ref_var_from_user((struct fs_info*)userInfo, info); + if (status != B_OK) + return status; return fs_write_info(device, &info, mask); } @@ -8861,9 +8864,6 @@ _user_get_next_fd_info(team_id team, uint32* userCookie, fd_info* userInfo, if (geteuid() != 0) return B_NOT_ALLOWED; - if (infoSize != sizeof(fd_info)) - return B_BAD_VALUE; - if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) || user_memcpy(&cookie, userCookie, sizeof(uint32)) != B_OK) return B_BAD_ADDRESS; @@ -8872,11 +8872,9 @@ _user_get_next_fd_info(team_id team, uint32* userCookie, fd_info* userInfo, if (status != B_OK) return status; - if (user_memcpy(userCookie, &cookie, sizeof(uint32)) != B_OK - || user_memcpy(userInfo, &info, infoSize) != B_OK) + if (user_memcpy(userCookie, &cookie, sizeof(uint32)) != B_OK) return B_BAD_ADDRESS; - - return status; + return copy_ref_var_to_user(info, userInfo); } @@ -9527,7 +9525,7 @@ _user_read_stat(int fd, const char* userPath, bool traverseLink, if (status != B_OK) return status; - return user_memcpy(userStat, &stat, statSize); + return copy_ref_var_to_user(stat, userStat, statSize); } @@ -9698,8 +9696,7 @@ _user_stat_attr(int fd, const char* userAttribute, info.type = stat.st_type; info.size = stat.st_size; - if (user_memcpy(userAttrInfo, &info, sizeof(struct attr_info)) != B_OK) - return B_BAD_ADDRESS; + status = copy_ref_var_to_user(info, userAttrInfo); } return status; @@ -9808,10 +9805,8 @@ _user_read_index_stat(dev_t device, const char* userName, struct stat* userStat) return B_BAD_ADDRESS; status = index_name_read_stat(device, name, &stat, false); - if (status == B_OK) { - if (user_memcpy(userStat, &stat, sizeof(stat)) != B_OK) - return B_BAD_ADDRESS; - } + if (status == B_OK) + status = copy_ref_var_to_user(stat, userStat); return status; }