diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/libpq/be-fsstubs.c | 101 | ||||
-rw-r--r-- | src/backend/storage/large_object/inv_api.c | 47 | ||||
-rw-r--r-- | src/backend/utils/errcodes.txt | 1 |
3 files changed, 127 insertions, 22 deletions
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 6f7e474f675..4bc81ba9f4d 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -39,6 +39,7 @@ #include "postgres.h" #include <fcntl.h> +#include <limits.h> #include <sys/stat.h> #include <unistd.h> @@ -216,7 +217,7 @@ lo_lseek(PG_FUNCTION_ARGS) int32 fd = PG_GETARG_INT32(0); int32 offset = PG_GETARG_INT32(1); int32 whence = PG_GETARG_INT32(2); - int status; + int64 status; if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL) ereport(ERROR, @@ -225,9 +226,45 @@ lo_lseek(PG_FUNCTION_ARGS) status = inv_seek(cookies[fd], offset, whence); + if (INT_MAX < status) + { + ereport(ERROR, + (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW), + errmsg("offset overflow: %d", fd))); + PG_RETURN_INT32(-1); + } + PG_RETURN_INT32(status); } + +Datum +lo_lseek64(PG_FUNCTION_ARGS) +{ + int32 fd = PG_GETARG_INT32(0); + int64 offset = PG_GETARG_INT64(1); + int32 whence = PG_GETARG_INT32(2); + MemoryContext currentContext; + int64 status; + + if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + PG_RETURN_INT64(-1); + } + + Assert(fscxt != NULL); + currentContext = MemoryContextSwitchTo(fscxt); + + status = inv_seek(cookies[fd], offset, whence); + + MemoryContextSwitchTo(currentContext); + + PG_RETURN_INT64(status); +} + Datum lo_creat(PG_FUNCTION_ARGS) { @@ -264,13 +301,46 @@ Datum lo_tell(PG_FUNCTION_ARGS) { int32 fd = PG_GETARG_INT32(0); + int64 offset = 0; + + if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + + offset = inv_tell(cookies[fd]); + + if (INT_MAX < offset) + { + ereport(ERROR, + (errcode(ERRCODE_BLOB_OFFSET_OVERFLOW), + errmsg("offset overflow: %d", fd))); + PG_RETURN_INT32(-1); + } + + PG_RETURN_INT32(offset); +} + + +Datum +lo_tell64(PG_FUNCTION_ARGS) +{ + int32 fd = PG_GETARG_INT32(0); if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL) + { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + PG_RETURN_INT64(-1); + } - PG_RETURN_INT32(inv_tell(cookies[fd])); + /* + * We assume we do not need to switch contexts for inv_tell. That is + * true for now, but is probably more than this module ought to + * assume... + */ + PG_RETURN_INT64(inv_tell(cookies[fd])); } Datum @@ -533,6 +603,33 @@ lo_truncate(PG_FUNCTION_ARGS) PG_RETURN_INT32(0); } +Datum +lo_truncate64(PG_FUNCTION_ARGS) +{ + int32 fd = PG_GETARG_INT32(0); + int64 len = PG_GETARG_INT64(1); + + if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + + /* Permission checks */ + if (!lo_compat_privileges && + pg_largeobject_aclcheck_snapshot(cookies[fd]->id, + GetUserId(), + ACL_UPDATE, + cookies[fd]->snapshot) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for large object %u", + cookies[fd]->id))); + + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +} + /* * AtEOXact_LargeObject - * prepares large objects for transaction commit diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index 3adfb159b8b..3f5688b939b 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -324,10 +324,10 @@ inv_drop(Oid lobjId) * NOTE: LOs can contain gaps, just like Unix files. We actually return * the offset of the last byte + 1. */ -static uint32 +static uint64 inv_getsize(LargeObjectDesc *obj_desc) { - uint32 lastbyte = 0; + uint64 lastbyte = 0; ScanKeyData skey[1]; SysScanDesc sd; HeapTuple tuple; @@ -368,7 +368,7 @@ inv_getsize(LargeObjectDesc *obj_desc) heap_tuple_untoast_attr((struct varlena *) datafield); pfreeit = true; } - lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield); + lastbyte = (uint64) data->pageno * LOBLKSIZE + getbytealen(datafield); if (pfreeit) pfree(datafield); } @@ -378,30 +378,31 @@ inv_getsize(LargeObjectDesc *obj_desc) return lastbyte; } -int -inv_seek(LargeObjectDesc *obj_desc, int offset, int whence) +int64 +inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence) { Assert(PointerIsValid(obj_desc)); switch (whence) { case SEEK_SET: - if (offset < 0) - elog(ERROR, "invalid seek offset: %d", offset); + if (offset < 0 || offset >= MAX_LARGE_OBJECT_SIZE) + elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset); obj_desc->offset = offset; break; case SEEK_CUR: - if (offset < 0 && obj_desc->offset < ((uint32) (-offset))) - elog(ERROR, "invalid seek offset: %d", offset); + if ((offset + obj_desc->offset) < 0 || + (offset + obj_desc->offset) >= MAX_LARGE_OBJECT_SIZE) + elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset); obj_desc->offset += offset; break; case SEEK_END: { - uint32 size = inv_getsize(obj_desc); + int64 pos = inv_getsize(obj_desc) + offset; - if (offset < 0 && size < ((uint32) (-offset))) - elog(ERROR, "invalid seek offset: %d", offset); - obj_desc->offset = size + offset; + if (pos < 0 || pos >= MAX_LARGE_OBJECT_SIZE) + elog(ERROR, "invalid seek offset: " INT64_FORMAT, offset); + obj_desc->offset = pos; } break; default: @@ -410,7 +411,7 @@ inv_seek(LargeObjectDesc *obj_desc, int offset, int whence) return obj_desc->offset; } -int +int64 inv_tell(LargeObjectDesc *obj_desc) { Assert(PointerIsValid(obj_desc)); @@ -422,11 +423,11 @@ int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) { int nread = 0; - int n; - int off; + int64 n; + int64 off; int len; int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE); - uint32 pageoff; + uint64 pageoff; ScanKeyData skey[2]; SysScanDesc sd; HeapTuple tuple; @@ -437,6 +438,9 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) if (nbytes <= 0) return 0; + if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE) + elog(ERROR, "invalid read request size: %d", nbytes); + open_lo_relation(); ScanKeyInit(&skey[0], @@ -467,7 +471,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) * there may be missing pages if the LO contains unwritten "holes". We * want missing sections to read out as zeroes. */ - pageoff = ((uint32) data->pageno) * LOBLKSIZE; + pageoff = ((uint64) data->pageno) * LOBLKSIZE; if (pageoff > obj_desc->offset) { n = pageoff - obj_desc->offset; @@ -560,6 +564,9 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) if (nbytes <= 0) return 0; + if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE) + elog(ERROR, "invalid write request size: %d", nbytes); + open_lo_relation(); indstate = CatalogOpenIndexes(lo_heap_r); @@ -718,10 +725,10 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) } void -inv_truncate(LargeObjectDesc *obj_desc, int len) +inv_truncate(LargeObjectDesc *obj_desc, int64 len) { int32 pageno = (int32) (len / LOBLKSIZE); - int off; + int32 off; ScanKeyData skey[2]; SysScanDesc sd; HeapTuple oldtuple; diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt index 3e041649563..db8ab53af26 100644 --- a/src/backend/utils/errcodes.txt +++ b/src/backend/utils/errcodes.txt @@ -199,6 +199,7 @@ Section: Class 22 - Data Exception 2200N E ERRCODE_INVALID_XML_CONTENT invalid_xml_content 2200S E ERRCODE_INVALID_XML_COMMENT invalid_xml_comment 2200T E ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION invalid_xml_processing_instruction +22P07 E ERRCODE_BLOB_OFFSET_OVERFLOW blob_offset_overflow Section: Class 23 - Integrity Constraint Violation |