8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 03:23:04 +01:00

Fixed CORE-6432: Possible buffer overflow in client library in Attachment::getInfo() call

This commit is contained in:
AlexPeshkoff 2020-10-28 20:46:27 +03:00
parent ca77a37910
commit f88c95acb6
3 changed files with 62 additions and 32 deletions

View File

@ -86,7 +86,8 @@ void ClumpletReader::dump() const
try { try {
ClumpletDump d(kind, getBuffer(), getBufferLength()); ClumpletDump d(kind, getBuffer(), getBufferLength());
int t = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged || kind == SpbResponse) ? -1 : d.getBufferTag(); int t = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged || kind == SpbResponse || kind == InfoResponse) ?
-1 : d.getBufferTag();
gds__log("Tag=%d Offset=%d Length=%d Eof=%d\n", t, getCurOffset(), getBufferLength(), isEof()); gds__log("Tag=%d Offset=%d Length=%d Eof=%d\n", t, getCurOffset(), getBufferLength(), isEof());
for (d.rewind(); !(d.isEof()); d.moveNext()) for (d.rewind(); !(d.isEof()); d.moveNext())
{ {
@ -240,6 +241,7 @@ UCHAR ClumpletReader::getBufferTag() const
case SpbSendItems: case SpbSendItems:
case SpbReceiveItems: case SpbReceiveItems:
case SpbResponse: case SpbResponse:
case InfoResponse:
usage_mistake("buffer is not tagged"); usage_mistake("buffer is not tagged");
return 0; return 0;
case SpbAttach: case SpbAttach:
@ -535,6 +537,16 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
} }
invalid_structure("unrecognized service response tag", tag); invalid_structure("unrecognized service response tag", tag);
break; break;
case InfoResponse:
switch (tag)
{
case isc_info_end:
case isc_info_truncated:
case isc_info_data_not_ready:
case isc_info_flag_end:
return SingleTpb;
}
return StringSpb;
} }
invalid_structure("unknown clumplet kind", kind); invalid_structure("unknown clumplet kind", kind);
return SingleTpb; return SingleTpb;
@ -688,6 +700,7 @@ void ClumpletReader::rewind()
case SpbSendItems: case SpbSendItems:
case SpbReceiveItems: case SpbReceiveItems:
case SpbResponse: case SpbResponse:
case InfoResponse:
cur_offset = 0; cur_offset = 0;
break; break;
default: default:

View File

@ -57,7 +57,8 @@ public:
WideUnTagged, WideUnTagged,
SpbSendItems, SpbSendItems,
SpbReceiveItems, SpbReceiveItems,
SpbResponse SpbResponse,
InfoResponse
}; };
struct KindList struct KindList
@ -130,7 +131,8 @@ public:
FB_SIZE_T rc = getBufferEnd() - getBuffer(); FB_SIZE_T rc = getBufferEnd() - getBuffer();
if (rc == 1 && kind != UnTagged && kind != SpbStart && if (rc == 1 && kind != UnTagged && kind != SpbStart &&
kind != WideUnTagged && kind != SpbSendItems && kind != WideUnTagged && kind != SpbSendItems &&
kind != SpbReceiveItems && kind != SpbResponse) kind != SpbReceiveItems && kind != SpbResponse &&
kind != InfoResponse)
{ {
rc = 0; rc = 0;
} }

View File

@ -37,12 +37,12 @@ inline void PUT_WORD(UCHAR*& ptr, USHORT value)
#define PUT(ptr, value) *(ptr)++ = value; #define PUT(ptr, value) *(ptr)++ = value;
static ISC_STATUS merge_setup(const UCHAR**, UCHAR**, const UCHAR* const, USHORT); static ISC_STATUS merge_setup(const Firebird::ClumpletReader&, UCHAR**, const UCHAR* const, FB_SIZE_T);
USHORT MERGE_database_info(const UCHAR* in, USHORT MERGE_database_info(const UCHAR* const in,
UCHAR* out, UCHAR* out,
USHORT out_length, USHORT buf_length,
USHORT impl, USHORT impl,
USHORT class_, USHORT class_,
USHORT base_level, USHORT base_level,
@ -65,22 +65,37 @@ USHORT MERGE_database_info(const UCHAR* in,
const UCHAR* p; const UCHAR* p;
UCHAR* start = out; UCHAR* start = out;
const UCHAR* const end = out + out_length; const UCHAR* const end = out + buf_length;
UCHAR mergeLevel = 0; UCHAR mergeLevel = 0;
for (const UCHAR* getMergeLevel = in; Firebird::ClumpletReader input(Firebird::ClumpletReader::InfoResponse, in, buf_length);
*getMergeLevel != isc_info_end && *getMergeLevel != isc_info_truncated; while (!input.isEof())
getMergeLevel += (3 + gds__vax_integer(getMergeLevel + 1, 2)))
{ {
if (*getMergeLevel == isc_info_implementation) bool flStop = true;
switch(input.getClumpTag())
{ {
mergeLevel = getMergeLevel[3]; case isc_info_implementation:
mergeLevel = input.getBytes()[0];
break;
case isc_info_end:
case isc_info_truncated:
break;
default:
flStop = false;
break; break;
} }
if (flStop)
break;
input.moveNext();
} }
for (;;) for (input.rewind(); !input.isEof(); input.moveNext())
switch (*out++ = *in++) {
*out++ = input.getClumpTag();
switch (input.getClumpTag())
{ {
case isc_info_end: case isc_info_end:
case isc_info_truncated: case isc_info_truncated:
@ -90,7 +105,7 @@ USHORT MERGE_database_info(const UCHAR* in,
l = static_cast<SSHORT>(strlen((char *) (p = version))); l = static_cast<SSHORT>(strlen((char *) (p = version)));
if (l > MAX_UCHAR) if (l > MAX_UCHAR)
l = MAX_UCHAR; l = MAX_UCHAR;
if (merge_setup(&in, &out, end, l + 1)) if (merge_setup(input, &out, end, l + 1))
return 0; return 0;
for (*out++ = (UCHAR) l; l; --l) for (*out++ = (UCHAR) l; l; --l)
*out++ = *p++; *out++ = *p++;
@ -100,21 +115,21 @@ USHORT MERGE_database_info(const UCHAR* in,
l = static_cast<SSHORT>(strlen((SCHAR *) (p = id))); l = static_cast<SSHORT>(strlen((SCHAR *) (p = id)));
if (l > MAX_UCHAR) if (l > MAX_UCHAR)
l = MAX_UCHAR; l = MAX_UCHAR;
if (merge_setup(&in, &out, end, l + 1)) if (merge_setup(input, &out, end, l + 1))
return 0; return 0;
for (*out++ = (UCHAR) l; l; --l) for (*out++ = (UCHAR) l; l; --l)
*out++ = *p++; *out++ = *p++;
break; break;
case isc_info_implementation: case isc_info_implementation:
if (merge_setup(&in, &out, end, 2)) if (merge_setup(input, &out, end, 2))
return 0; return 0;
PUT(out, (UCHAR) impl); PUT(out, (UCHAR) impl);
PUT(out, (UCHAR) class_); PUT(out, (UCHAR) class_);
break; break;
case fb_info_implementation: case fb_info_implementation:
if (merge_setup(&in, &out, end, 6)) if (merge_setup(input, &out, end, 6))
return 0; return 0;
Firebird::DbImplementation::current.stuff(&out); Firebird::DbImplementation::current.stuff(&out);
PUT(out, (UCHAR) class_); PUT(out, (UCHAR) class_);
@ -122,30 +137,32 @@ USHORT MERGE_database_info(const UCHAR* in,
break; break;
case isc_info_base_level: case isc_info_base_level:
if (merge_setup(&in, &out, end, 1)) if (merge_setup(input, &out, end, 1))
return 0; return 0;
PUT(out, (UCHAR) base_level); PUT(out, (UCHAR) base_level);
break; break;
default: default:
{ {
USHORT length = (USHORT) gds__vax_integer(in, 2); USHORT length = input.getClumpLength();
in += 2;
if (out + length + 2 >= end) if (out + length + 2 >= end)
{ {
out[-1] = isc_info_truncated; out[-1] = isc_info_truncated;
return 0; return 0;
} }
PUT_WORD(out, length); PUT_WORD(out, length);
while (length--) memcpy(out, input.getBytes(), length);
*out++ = *in++; out += length;
} }
break; break;
} }
}
return 0; // error - missing isc_info_end item
} }
static ISC_STATUS merge_setup(const UCHAR** in, UCHAR** out, const UCHAR* const end, static ISC_STATUS merge_setup(const Firebird::ClumpletReader& input, UCHAR** out, const UCHAR* const end,
USHORT delta_length) FB_SIZE_T delta_length)
{ {
/************************************** /**************************************
* *
@ -159,17 +176,16 @@ static ISC_STATUS merge_setup(const UCHAR** in, UCHAR** out, const UCHAR* const
* already there. * already there.
* *
**************************************/ **************************************/
USHORT length = (USHORT) gds__vax_integer(*in, 2); FB_SIZE_T length = input.getClumpLength();
const USHORT new_length = length + delta_length; const FB_SIZE_T new_length = length + delta_length;
if (*out + new_length + 2 >= end) if (new_length > MAX_USHORT || *out + new_length + 2 >= end)
{ {
(*out)[-1] = isc_info_truncated; (*out)[-1] = isc_info_truncated;
return FB_FAILURE; return FB_FAILURE;
} }
*in += 2; const USHORT count = 1 + *(input.getBytes());
const USHORT count = 1 + *(*in)++;
PUT_WORD(*out, new_length); PUT_WORD(*out, new_length);
PUT(*out, (UCHAR) count); PUT(*out, (UCHAR) count);
@ -177,9 +193,8 @@ static ISC_STATUS merge_setup(const UCHAR** in, UCHAR** out, const UCHAR* const
if (--length) if (--length)
{ {
memcpy(*out, *in, length); memcpy(*out, input.getBytes() + 1, length);
*out += length; *out += length;
*in += length;
} }
return FB_SUCCESS; return FB_SUCCESS;