diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index 2a7055a0..cb7a3f64 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -119,6 +119,32 @@ Ref OpenDirContainingPath(ioctx_t* ctx, return ret; } +size_t TruncateIOVec(struct iovec* iov, int iovcnt, off_t limit) +{ + assert(0 <= iovcnt); + assert(0 <= limit); + off_t left = limit; + if ( (uintmax_t) SSIZE_MAX <= (uintmax_t) left ) + left = SSIZE_MAX; + size_t requested = 0; + for ( int i = 0; i < iovcnt; i++ ) + { + size_t request = iov[i].iov_len; + if ( __builtin_add_overflow(requested, request, &requested) ) + requested = SIZE_MAX; + if ( left == 0 ) + iov[i].iov_len = 0; + else if ( (uintmax_t) left < (uintmax_t) request ) + { + iov[i].iov_len = left; + left = 0; + } + else + left -= request; + } + return requested; +} + // TODO: Add security checks. Descriptor::Descriptor() @@ -192,13 +218,14 @@ Ref Descriptor::Fork() bool Descriptor::IsSeekable() { + if ( S_ISCHR(type) ) + return false; ScopedLock lock(¤t_offset_lock); if ( !checked_seekable ) { - // TODO: Is this enough? Check that errno happens to be ESPIPE? int saved_errno = errno; ioctx_t ctx; SetupKernelIOCtx(&ctx); - seekable = S_ISDIR(vnode->type) || 0 <= vnode->lseek(&ctx, SEEK_SET, 0); + seekable = S_ISDIR(vnode->type) || 0 <= vnode->lseek(&ctx, 0, SEEK_END); checked_seekable = true; errno = saved_errno; } @@ -242,18 +269,26 @@ int Descriptor::truncate(ioctx_t* ctx, off_t length) if ( length < 0 ) return errno = EINVAL, -1; if ( !(dflags & O_WRITE) ) - return errno = EPERM, -1; + return errno = EBADF, -1; return vnode->truncate(ctx, length); } off_t Descriptor::lseek(ioctx_t* ctx, off_t offset, int whence) { - // TODO: Possible information leak to let someone without O_READ | O_WRITE - // seek the file and get information about data holes. + if ( S_ISCHR(type) ) + { + if ( whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END ) + return errno = EINVAL, -1; + return 0; + } + if ( !IsSeekable() ) - return vnode->lseek(ctx, offset, whence); + return errno = ESPIPE, -1; ScopedLock lock(¤t_offset_lock); + + // TODO: Possible information leak to let someone without O_READ | O_WRITE + // seek the file and get information about data holes. off_t reloff; if ( whence == SEEK_SET ) reloff = 0; @@ -261,48 +296,51 @@ off_t Descriptor::lseek(ioctx_t* ctx, off_t offset, int whence) reloff = current_offset; else if ( whence == SEEK_END ) { - if ( (reloff = vnode->lseek(ctx, offset, SEEK_END)) < 0 ) + if ( (reloff = vnode->lseek(ctx, 0, SEEK_END)) < 0 ) return -1; } else return errno = EINVAL, -1; - if ( offset < 0 && reloff + offset < 0 ) - return errno = EOVERFLOW, -1; - if ( OFF_MAX - current_offset < offset ) + off_t new_offset; + if ( __builtin_add_overflow(reloff, offset, &new_offset) ) return errno = EOVERFLOW, -1; + if ( new_offset < 0 ) + return errno = EINVAL, -1; - return current_offset = reloff + offset; + return current_offset = new_offset; } ssize_t Descriptor::read(ioctx_t* ctx, uint8_t* buf, size_t count) { if ( !(dflags & O_READ) ) - return errno = EPERM, -1; - if ( !count ) - return 0; + return errno = EBADF, -1; if ( SIZE_MAX < count ) count = SSIZE_MAX; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); + ssize_t result; if ( !IsSeekable() ) + result = vnode->read(ctx, buf, count); + else { - ssize_t result = vnode->read(ctx, buf, count); - ctx->dflags = old_ctx_dflags; - return result; + ScopedLock lock(¤t_offset_lock); + off_t available = OFF_MAX - current_offset; + if ( (uintmax_t) available < (uintmax_t) count ) + count = available; + result = vnode->pread(ctx, buf, count, current_offset); + if ( 0 < result && + __builtin_add_overflow(current_offset, result, ¤t_offset) ) + current_offset = OFF_MAX; } - ScopedLock lock(¤t_offset_lock); - ssize_t ret = vnode->pread(ctx, buf, count, current_offset); - if ( 0 <= ret ) - current_offset += ret; ctx->dflags = old_ctx_dflags; - return ret; + return result; } ssize_t Descriptor::readv(ioctx_t* ctx, const struct iovec* iov_ptr, int iovcnt) { if ( !(dflags & O_READ) ) - return errno = EPERM, -1; + return errno = EBADF, -1; if ( iovcnt < 0 || IOV_MAX < iovcnt ) return errno = EINVAL, -1; struct iovec* iov = new struct iovec[iovcnt]; @@ -313,32 +351,46 @@ ssize_t Descriptor::readv(ioctx_t* ctx, const struct iovec* iov_ptr, int iovcnt) return delete[] iov, -1; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); + ssize_t result = -1; if ( !IsSeekable() ) { - ssize_t result = vnode->readv(ctx, iov, iovcnt); - ctx->dflags = old_ctx_dflags; - delete[] iov; - return result; + if ( SSIZE_MAX < TruncateIOVec(iov, iovcnt, SSIZE_MAX) ) + errno = EINVAL; + else + result = vnode->readv(ctx, iov, iovcnt); + } + else + { + ScopedLock lock(¤t_offset_lock); + off_t available = OFF_MAX - current_offset; + if ( SSIZE_MAX < TruncateIOVec(iov, iovcnt, available) ) + errno = EINVAL; + else + result = vnode->preadv(ctx, iov, iovcnt, current_offset); + if ( 0 < result && + __builtin_add_overflow(current_offset, result, ¤t_offset) ) + current_offset = OFF_MAX; } - ScopedLock lock(¤t_offset_lock); - ssize_t ret = vnode->preadv(ctx, iov, iovcnt, current_offset); - if ( 0 <= ret ) - current_offset += ret; ctx->dflags = old_ctx_dflags; delete[] iov; - return ret; + return result; } ssize_t Descriptor::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) { + if ( S_ISCHR(type) ) + return read(ctx, buf, count); if ( !(dflags & O_READ) ) - return errno = EPERM, -1; + return errno = EBADF, -1; + if ( !IsSeekable() ) + return errno = ESPIPE, -1; if ( off < 0 ) return errno = EINVAL, -1; - if ( !count ) - return 0; if ( SSIZE_MAX < count ) count = SSIZE_MAX; + off_t available = OFF_MAX - off; + if ( (uintmax_t) available < (uintmax_t) count ) + count = available; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); ssize_t result = vnode->pread(ctx, buf, count, off); @@ -349,8 +401,12 @@ ssize_t Descriptor::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) ssize_t Descriptor::preadv(ioctx_t* ctx, const struct iovec* iov_ptr, int iovcnt, off_t off) { + if ( S_ISCHR(type) ) + return readv(ctx, iov_ptr, iovcnt); if ( !(dflags & O_READ) ) - return errno = EPERM, -1; + return errno = EBADF, -1; + if ( !IsSeekable() ) + return errno = ESPIPE, -1; if ( off < 0 || iovcnt < 0 || IOV_MAX < iovcnt ) return errno = EINVAL, -1; struct iovec* iov = new struct iovec[iovcnt]; @@ -359,6 +415,9 @@ ssize_t Descriptor::preadv(ioctx_t* ctx, const struct iovec* iov_ptr, size_t iov_size = sizeof(struct iovec) * iovcnt; if ( !ctx->copy_from_src(iov, iov_ptr, iov_size) ) return delete[] iov, -1; + off_t available = OFF_MAX - off; + if ( SSIZE_MAX < TruncateIOVec(iov, iovcnt, available) ) + return delete[] iov, errno = EINVAL, -1; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); ssize_t result = vnode->preadv(ctx, iov, iovcnt, off); @@ -370,42 +429,49 @@ ssize_t Descriptor::preadv(ioctx_t* ctx, const struct iovec* iov_ptr, ssize_t Descriptor::write(ioctx_t* ctx, const uint8_t* buf, size_t count) { if ( !(dflags & O_WRITE) ) - return errno = EPERM, -1; - if ( !count ) - return 0; + return errno = EBADF, -1; if ( SSIZE_MAX < count ) count = SSIZE_MAX; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); + ssize_t result; if ( !IsSeekable() ) + result = vnode->write(ctx, buf, count); + else { - ssize_t result = vnode->write(ctx, buf, count); - ctx->dflags = old_ctx_dflags; - return result; - } - ScopedLock lock(¤t_offset_lock); - if ( ctx->dflags & O_APPEND ) - { - off_t end = vnode->lseek(ctx, 0, SEEK_END); - if ( end < 0 ) + ScopedLock lock(¤t_offset_lock); + if ( ctx->dflags & O_APPEND ) { - ctx->dflags = old_ctx_dflags; - return -1; + off_t end = vnode->lseek(ctx, 0, SEEK_END); + if ( 0 <= end ) + current_offset = end; + } + if ( current_offset == OFF_MAX && count ) + { + errno = EFBIG; + result = -1; + } + else + { + off_t available = OFF_MAX - current_offset; + if ( (uintmax_t) available < (uintmax_t) count ) + count = available; + result = vnode->pwrite(ctx, buf, count, current_offset); + if ( 0 < result && + __builtin_add_overflow(current_offset, result, + ¤t_offset) ) + current_offset = OFF_MAX; } - current_offset = end; } - ssize_t ret = vnode->pwrite(ctx, buf, count, current_offset); - if ( 0 <= ret ) - current_offset += ret; ctx->dflags = old_ctx_dflags; - return ret; + return result; } ssize_t Descriptor::writev(ioctx_t* ctx, const struct iovec* iov_ptr, int iovcnt) { if ( !(dflags & O_WRITE) ) - return errno = EPERM, -1; + return errno = EBADF, -1; if ( iovcnt < 0 || IOV_MAX < iovcnt ) return errno = EINVAL, -1; struct iovec* iov = new struct iovec[iovcnt]; @@ -416,43 +482,61 @@ ssize_t Descriptor::writev(ioctx_t* ctx, const struct iovec* iov_ptr, return delete[] iov, -1; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); + ssize_t result = -1; if ( !IsSeekable() ) { - ssize_t result = vnode->writev(ctx, iov, iovcnt); - ctx->dflags = old_ctx_dflags; - delete[] iov; - return result; + if ( SSIZE_MAX < TruncateIOVec(iov, iovcnt, SSIZE_MAX) ) + errno = EINVAL; + else + result = vnode->writev(ctx, iov, iovcnt); } - ScopedLock lock(¤t_offset_lock); - if ( ctx->dflags & O_APPEND ) + else { - off_t end = vnode->lseek(ctx, 0, SEEK_END); - if ( end < 0 ) + ScopedLock lock(¤t_offset_lock); + if ( ctx->dflags & O_APPEND ) { - ctx->dflags = old_ctx_dflags; - return -1; + off_t end = vnode->lseek(ctx, 0, SEEK_END); + if ( 0 <= end ) + current_offset = end; + } + off_t available = OFF_MAX - current_offset; + size_t count = TruncateIOVec(iov, iovcnt, available); + if ( SSIZE_MAX < count ) + errno = EINVAL; + else if ( current_offset == OFF_MAX && count ) + errno = EFBIG; + else + { + result = vnode->pwritev(ctx, iov, iovcnt, current_offset); + if ( 0 < result && + __builtin_add_overflow(current_offset, result, + ¤t_offset) ) + current_offset = OFF_MAX; } - current_offset = end; } - ssize_t ret = vnode->pwritev(ctx, iov, iovcnt, current_offset); - if ( 0 <= ret ) - current_offset += ret; ctx->dflags = old_ctx_dflags; delete[] iov; - return ret; + return result; } ssize_t Descriptor::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) { + if ( S_ISCHR(type) ) + return write(ctx, buf, count); if ( !(dflags & O_WRITE) ) - return errno = EPERM, -1; + return errno = EBADF, -1; + if ( !IsSeekable() ) + return errno = ESPIPE, -1; if ( off < 0 ) return errno = EINVAL, -1; - if ( !count ) - return 0; + if ( off == OFF_MAX && count ) + return errno = EFBIG, -1; if ( SSIZE_MAX < count ) count = SSIZE_MAX; + off_t available = OFF_MAX - off; + if ( (uintmax_t) available < (uintmax_t) count ) + count = available; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); ssize_t result = vnode->pwrite(ctx, buf, count, off); @@ -463,8 +547,12 @@ ssize_t Descriptor::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, ssize_t Descriptor::pwritev(ioctx_t* ctx, const struct iovec* iov_ptr, int iovcnt, off_t off) { + if ( S_ISCHR(type) ) + return writev(ctx, iov_ptr, iovcnt); if ( !(dflags & O_WRITE) ) - return errno = EPERM, -1; + return errno = EBADF, -1; + if ( !IsSeekable() ) + return errno = ESPIPE, -1; if ( off < 0 || iovcnt < 0 || IOV_MAX < iovcnt ) return errno = EINVAL, -1; struct iovec* iov = new struct iovec[iovcnt]; @@ -473,6 +561,12 @@ ssize_t Descriptor::pwritev(ioctx_t* ctx, const struct iovec* iov_ptr, size_t iov_size = sizeof(struct iovec) * iovcnt; if ( !ctx->copy_from_src(iov, iov_ptr, iov_size) ) return delete[] iov, -1; + off_t available = OFF_MAX - off; + size_t count = TruncateIOVec(iov, iovcnt, available); + if ( SSIZE_MAX < count ) + return delete[] iov, errno = EINVAL, -1; + if ( off == OFF_MAX && count != 0 ) + return delete[] iov, errno = EFBIG, -1; int old_ctx_dflags = ctx->dflags; ctx->dflags = ContextFlags(old_ctx_dflags, dflags); ssize_t result = vnode->pwritev(ctx, iov, iovcnt, off); @@ -529,15 +623,18 @@ ssize_t Descriptor::readdirents(ioctx_t* ctx, // it with the O_EXEC flag - POSIX allows that and it makes sense // because the execute bit on directories control search permission. if ( !(dflags & (O_SEARCH | O_READ | O_WRITE)) ) - return errno = EPERM, -1; + return errno = EBADF, -1; if ( SSIZE_MAX < size ) size = SSIZE_MAX; if ( size < sizeof(*dirent) ) return errno = EINVAL, -1; ScopedLock lock(¤t_offset_lock); + if ( current_offset == OFF_MAX && size ) + return 0; ssize_t ret = vnode->readdirents(ctx, dirent, size, current_offset); - if ( 0 < ret ) - current_offset++; + if ( 0 < ret && + __builtin_add_overflow(current_offset, 1, ¤t_offset) ) + current_offset = OFF_MAX; return ret; } @@ -803,7 +900,7 @@ int Descriptor::rename_here(ioctx_t* ctx, Ref from, ssize_t Descriptor::readlink(ioctx_t* ctx, char* buf, size_t bufsize) { if ( !(dflags & O_READ) ) - return errno = EPERM, -1; + return errno = EBADF, -1; if ( SSIZE_MAX < bufsize ) bufsize = SSIZE_MAX; return vnode->readlink(ctx, buf, bufsize); diff --git a/kernel/disk/node.cpp b/kernel/disk/node.cpp index cc75636b..884e9312 100644 --- a/kernel/disk/node.cpp +++ b/kernel/disk/node.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2017 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -71,15 +71,8 @@ int PortNode::truncate(ioctx_t* /*ctx*/, off_t length) off_t PortNode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) { - if ( whence == SEEK_SET ) - return offset; - if ( whence == SEEK_END ) - { - off_t result; - if ( __builtin_add_overflow(harddisk->GetSize(), offset, &result) ) - return errno = EOVERFLOW, -1; - return result; - } + if ( whence == SEEK_END && offset == 0 ) + return (off_t) harddisk->GetSize(); return errno = EINVAL, -1; } diff --git a/kernel/fcache.cpp b/kernel/fcache.cpp index 4513374d..7c5b3c51 100644 --- a/kernel/fcache.cpp +++ b/kernel/fcache.cpp @@ -439,10 +439,8 @@ off_t FileCache::GetFileSize() off_t FileCache::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) { ScopedLock lock(&fcache_mutex); - if ( whence == SEEK_SET ) - return offset; - if ( whence == SEEK_END ) - return (off_t) file_size + offset; + if ( whence == SEEK_END && offset == 0 ) + return (off_t) file_size; return errno = EINVAL, -1; } diff --git a/kernel/fs/full.cpp b/kernel/fs/full.cpp index 8913f913..64a5a63d 100644 --- a/kernel/fs/full.cpp +++ b/kernel/fs/full.cpp @@ -53,16 +53,6 @@ Full::~Full() { } -int Full::truncate(ioctx_t* /*ctx*/, off_t /*length*/) -{ - return 0; -} - -off_t Full::lseek(ioctx_t* /*ctx*/, off_t offset, int /*whence*/) -{ - return offset; -} - ssize_t Full::read(ioctx_t* ctx, uint8_t* buf, size_t count) { ctx->zero_dest(buf, count); diff --git a/kernel/fs/full.h b/kernel/fs/full.h index 1b0a0661..08986c75 100644 --- a/kernel/fs/full.h +++ b/kernel/fs/full.h @@ -29,8 +29,6 @@ class Full : public AbstractInode public: Full(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); virtual ~Full(); - virtual int truncate(ioctx_t* ctx, off_t length); - virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); diff --git a/kernel/fs/null.cpp b/kernel/fs/null.cpp index d75fefa5..51ef997c 100644 --- a/kernel/fs/null.cpp +++ b/kernel/fs/null.cpp @@ -50,16 +50,6 @@ Null::~Null() { } -int Null::truncate(ioctx_t* /*ctx*/, off_t /*length*/) -{ - return 0; -} - -off_t Null::lseek(ioctx_t* /*ctx*/, off_t offset, int /*whence*/) -{ - return offset; -} - ssize_t Null::read(ioctx_t* /*ctx*/, uint8_t* /*buf*/, size_t /*count*/) { return 0; @@ -77,7 +67,7 @@ ssize_t Null::write(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, size_t count) } ssize_t Null::pwrite(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, size_t count, - off_t /*off*/) + off_t /*off*/) { return count; } diff --git a/kernel/fs/null.h b/kernel/fs/null.h index c05df25d..ccce41df 100644 --- a/kernel/fs/null.h +++ b/kernel/fs/null.h @@ -29,8 +29,6 @@ class Null : public AbstractInode public: Null(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); virtual ~Null(); - virtual int truncate(ioctx_t* ctx, off_t length); - virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); diff --git a/kernel/fs/random.cpp b/kernel/fs/random.cpp index 46d6043c..b861b7cd 100644 --- a/kernel/fs/random.cpp +++ b/kernel/fs/random.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -32,7 +31,8 @@ namespace Sortix { -DevRandom::DevRandom(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode) +DevRandom::DevRandom(dev_t dev, ino_t ino, uid_t owner, gid_t group, + mode_t mode) { inode_type = INODE_TYPE_STREAM; if ( !dev ) @@ -53,16 +53,6 @@ DevRandom::~DevRandom() { } -int DevRandom::truncate(ioctx_t* /*ctx*/, off_t /*length*/) -{ - return 0; -} - -off_t DevRandom::lseek(ioctx_t* /*ctx*/, off_t offset, int /*whence*/) -{ - return offset; -} - ssize_t DevRandom::read(ioctx_t* ctx, uint8_t* buf, size_t count) { size_t sofar = 0; @@ -82,7 +72,8 @@ ssize_t DevRandom::read(ioctx_t* ctx, uint8_t* buf, size_t count) return (ssize_t) sofar; } -ssize_t DevRandom::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t /*off*/) +ssize_t DevRandom::pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t /*off*/) { return read(ctx, buf, count); } @@ -92,8 +83,8 @@ ssize_t DevRandom::write(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, size_t count) return count; } -ssize_t DevRandom::pwrite(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, size_t count, - off_t /*off*/) +ssize_t DevRandom::pwrite(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, + size_t count, off_t /*off*/) { return count; } diff --git a/kernel/fs/random.h b/kernel/fs/random.h index 3f6a2644..db1659ef 100644 --- a/kernel/fs/random.h +++ b/kernel/fs/random.h @@ -29,8 +29,6 @@ class DevRandom : public AbstractInode public: DevRandom(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); virtual ~DevRandom(); - virtual int truncate(ioctx_t* ctx, off_t length); - virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index c6d3af16..de4735ec 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -876,6 +876,8 @@ int Unode::truncate(ioctx_t* ctx, off_t length) off_t Unode::lseek(ioctx_t* ctx, off_t offset, int whence) { + if ( whence != SEEK_END && offset != 0 ) + return errno = EINVAL, -1; Channel* channel = server->Connect(ctx); if ( !channel ) return -1; diff --git a/kernel/fs/util.cpp b/kernel/fs/util.cpp index 3fbeabbc..1cb19a80 100644 --- a/kernel/fs/util.cpp +++ b/kernel/fs/util.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2017 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,6 +17,7 @@ * Utility classes for kernel filesystems. */ +#include #include #include @@ -36,8 +37,9 @@ namespace Sortix { UtilMemoryBuffer::UtilMemoryBuffer(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode, uint8_t* buf, - size_t bufsize, bool write, bool deletebuf) + off_t bufsize, bool write, bool deletebuf) { + assert(0 <= bufsize); inode_type = INODE_TYPE_FILE; this->filelock = KTHREAD_MUTEX_INITIALIZER; this->stat_uid = owner; @@ -45,7 +47,7 @@ UtilMemoryBuffer::UtilMemoryBuffer(dev_t dev, ino_t ino, uid_t owner, this->type = S_IFREG; this->stat_mode = (mode & S_SETABLE) | this->type; this->stat_blksize = 1; - this->stat_size = (off_t) bufsize; + this->stat_size = bufsize; this->dev = dev; this->ino = ino ? ino : (ino_t) this; this->buf = buf; @@ -63,30 +65,27 @@ UtilMemoryBuffer::~UtilMemoryBuffer() int UtilMemoryBuffer::truncate(ioctx_t* /*ctx*/, off_t length) { ScopedLock lock(&filelock); - if ( (uintmax_t) length != (uintmax_t) bufsize ) - return errno = ENOTSUP, -1; + if ( length != bufsize ) + return errno = EPERM, -1; return 0; } off_t UtilMemoryBuffer::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) { ScopedLock lock(&filelock); - if ( whence == SEEK_SET ) - return offset; - if ( whence == SEEK_END ) - return (off_t) bufsize + offset; - errno = EINVAL; - return -1; + if ( whence == SEEK_END && offset == 0 ) + return bufsize; + return errno = EINVAL, -1; } ssize_t UtilMemoryBuffer::pread(ioctx_t* ctx, uint8_t* dest, size_t count, off_t off) { ScopedLock lock(&filelock); - if ( (uintmax_t) bufsize < (uintmax_t) off ) + if ( bufsize < off ) return 0; - size_t available = bufsize - off; - if ( available < count ) + off_t available = bufsize - off; + if ( (uintmax_t) available < (uintmax_t) count ) count = available; if ( !ctx->copy_to_dest(dest, buf + off, count) ) return -1; @@ -97,16 +96,17 @@ ssize_t UtilMemoryBuffer::pwrite(ioctx_t* ctx, const uint8_t* src, size_t count, off_t off) { ScopedLock lock(&filelock); - if ( !write ) { errno = EBADF; return -1; } - // TODO: Avoid having off + count overflow! - if ( bufsize < off + count ) - return 0; - if ( (uintmax_t) bufsize <= (uintmax_t) off ) - return -1; - size_t available = bufsize - off; - if ( available < count ) + if ( !write ) + return errno = EROFS, -1; + if ( bufsize < off ) + return errno = EFBIG, -1; + off_t available = bufsize - off; + if ( available == 0 && count ) + return errno = EFBIG, -1; + if ( (uintmax_t) available < (uintmax_t) count ) count = available; - ctx->copy_from_src(buf + off, src, count); + if ( !ctx->copy_from_src(buf + off, src, count) ) + return -1; return count; } diff --git a/kernel/fs/util.h b/kernel/fs/util.h index 813e4ca6..b84c3284 100644 --- a/kernel/fs/util.h +++ b/kernel/fs/util.h @@ -28,7 +28,7 @@ class UtilMemoryBuffer : public AbstractInode { public: UtilMemoryBuffer(dev_t dev, ino_t ino, uid_t owner, gid_t group, - mode_t mode, uint8_t* buf, size_t bufsize, + mode_t mode, uint8_t* buf, off_t bufsize, bool write = true, bool deletebuf = true); virtual ~UtilMemoryBuffer(); virtual int truncate(ioctx_t* ctx, off_t length); @@ -41,7 +41,7 @@ public: private: kthread_mutex_t filelock; uint8_t* buf; - size_t bufsize; + off_t bufsize; bool write; bool deletebuf; diff --git a/kernel/fs/zero.cpp b/kernel/fs/zero.cpp index 4405b9a7..10469096 100644 --- a/kernel/fs/zero.cpp +++ b/kernel/fs/zero.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 2017 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -52,19 +52,10 @@ Zero::~Zero() { } -int Zero::truncate(ioctx_t* /*ctx*/, off_t /*length*/) -{ - return 0; -} - -off_t Zero::lseek(ioctx_t* /*ctx*/, off_t offset, int /*whence*/) -{ - return offset; -} - ssize_t Zero::read(ioctx_t* ctx, uint8_t* buf, size_t count) { - ctx->zero_dest(buf, count); + if ( !ctx->zero_dest(buf, count) ) + return -1; return (ssize_t) count; } diff --git a/kernel/fs/zero.h b/kernel/fs/zero.h index eea936f7..0730bf7b 100644 --- a/kernel/fs/zero.h +++ b/kernel/fs/zero.h @@ -29,8 +29,6 @@ class Zero : public AbstractInode public: Zero(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); virtual ~Zero(); - virtual int truncate(ioctx_t* ctx, off_t length); - virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); diff --git a/kernel/include/sortix/kernel/descriptor.h b/kernel/include/sortix/kernel/descriptor.h index 2feebda9..facc2102 100644 --- a/kernel/include/sortix/kernel/descriptor.h +++ b/kernel/include/sortix/kernel/descriptor.h @@ -150,6 +150,7 @@ int LinkInodeInDir(ioctx_t* ctx, Ref dir, const char* name, Ref inode); Ref OpenDirContainingPath(ioctx_t* ctx, Ref from, const char* path, char** finalp); +size_t TruncateIOVec(struct iovec* iov, int iovcnt, off_t limit); } // namespace Sortix diff --git a/kernel/inode.cpp b/kernel/inode.cpp index 1dacb5e8..7b14bdf6 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -146,9 +146,7 @@ int AbstractInode::truncate(ioctx_t* /*ctx*/, off_t /*length*/) off_t AbstractInode::lseek(ioctx_t* /*ctx*/, off_t /*offset*/, int /*whence*/) { - if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY ) - return errno = ESPIPE, -1; - return errno = EBADF, -1; + return errno = ESPIPE, -1; } ssize_t AbstractInode::read(ioctx_t* ctx, uint8_t* buf, size_t count) diff --git a/kernel/io.cpp b/kernel/io.cpp index 9a1e305a..9c785278 100644 --- a/kernel/io.cpp +++ b/kernel/io.cpp @@ -838,6 +838,14 @@ int sys_mkpartition(int fd, off_t start, off_t length, int flags) Ref inner_inode = desc->vnode->inode; desc.Reset(); + if ( !S_ISBLK(inner_inode->type) && !S_ISREG(inner_inode->type) ) + return errno = EPERM, -1; + if ( start < 0 || length < 0 ) + return errno = EINVAL, -1; + off_t end; + if ( __builtin_add_overflow(start, length, &end) ) + return errno = EOVERFLOW, -1; + Ref partition(new Partition(inner_inode, start, length)); if ( !partition ) return -1; diff --git a/kernel/memorymanagement.cpp b/kernel/memorymanagement.cpp index 43525562..6fe51192 100644 --- a/kernel/memorymanagement.cpp +++ b/kernel/memorymanagement.cpp @@ -327,7 +327,7 @@ void* sys_mmap(void* addr_ptr, size_t size, int prot, int flags, int fd, if ( !(desc = process->GetDescriptor(fd)) ) return MAP_FAILED; // Verify that the file is seekable. - if ( desc->lseek(&ctx, 0, SEEK_CUR) < 0 ) + if ( S_ISCHR(desc->type) || desc->lseek(&ctx, 0, SEEK_END) < 0 ) return errno = ENODEV, MAP_FAILED; // Verify that we have read access to the file. if ( desc->read(&ctx, NULL, 0) != 0 ) diff --git a/kernel/partition.cpp b/kernel/partition.cpp index eb2421ea..2e3e81a2 100644 --- a/kernel/partition.cpp +++ b/kernel/partition.cpp @@ -19,6 +19,7 @@ #include +#include #include #include @@ -35,6 +36,10 @@ namespace Sortix { Partition::Partition(Ref inner_inode, off_t start, off_t length) { + assert(0 <= start); + assert(0 <= length); + assert(length <= OFF_MAX - start); + assert(S_ISBLK(inner_inode->type) || S_ISREG(inner_inode->type)); this->dev = (dev_t) this; this->ino = (ino_t) this; this->type = inner_inode->type; @@ -62,33 +67,48 @@ int Partition::truncate(ioctx_t* /*ctx*/, off_t new_length) off_t Partition::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) { - if ( whence == SEEK_SET ) - return offset; - if ( whence == SEEK_END ) - // TODO: Avoid underflow and overflow! - return length + offset; + if ( whence == SEEK_END && offset == 0 ) + return length; return errno = EINVAL, -1; } ssize_t Partition::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) { + if ( off < 0 ) + return errno = EINVAL, -1; if ( length <= off ) return 0; off_t available = length - off; if ( (uintmax_t) available < (uintmax_t) count ) count = available; - return inner_inode->pread(ctx, buf, count, start + off); + if ( count == 0 ) + return 0; + off_t final_offset; + if ( __builtin_add_overflow(start, off, &final_offset) ) + return errno = EOVERFLOW, -1; + assert(start <= final_offset); + assert(final_offset < start + length); + return inner_inode->pread(ctx, buf, count, final_offset); } ssize_t Partition::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) { + if ( off < 0 ) + return errno = EINVAL, -1; if ( length <= off ) - return 0; + return count ? (errno = EFBIG, -1) : 0; off_t available = length - off; if ( (uintmax_t) available < (uintmax_t) count ) count = available; - return inner_inode->pwrite(ctx, buf, count, start + off); + if ( count == 0 ) + return 0; + off_t final_offset; + if ( __builtin_add_overflow(start, off, &final_offset) ) + return errno = EOVERFLOW, -1; + assert(start <= final_offset); + assert(final_offset < start + length); + return inner_inode->pwrite(ctx, buf, count, final_offset); } int Partition::stat(ioctx_t* ctx, struct stat* st) diff --git a/kernel/pipe.cpp b/kernel/pipe.cpp index ea1e7e3a..f7ba4d41 100644 --- a/kernel/pipe.cpp +++ b/kernel/pipe.cpp @@ -205,6 +205,8 @@ ssize_t PipeChannel::recvmsg_internal(ioctx_t* ctx, struct msghdr* msg, return errno = EINTR, -1; ssize_t so_far = 0; size_t peeked = 0; + if ( SSIZE_MAX < TruncateIOVec(msg->msg_iov, msg->msg_iovlen, SSIZE_MAX) ) + return errno = EINVAL, -1; int iov_i = 0; size_t iov_offset = 0; while ( iov_i < msg->msg_iovlen && so_far < SSIZE_MAX ) @@ -336,6 +338,8 @@ ssize_t PipeChannel::sendmsg_internal(ioctx_t* ctx, const struct msghdr* msg, if ( !lock.IsAcquired() ) return errno = EINTR, -1; sender_system_tid = this_thread->system_tid; + if ( SSIZE_MAX < TruncateIOVec(msg->msg_iov, msg->msg_iovlen, SSIZE_MAX) ) + return errno = EINVAL, -1; ssize_t so_far = 0; int iov_i = 0; size_t iov_offset = 0;