Fix bug #1494 potential for buffer overrun

The get/put routines now require the buffer size remaining to be read/written to permit checking and the iterator will update this value whenever iterator comparisions are made.  Other direct uses of get/put have been updated.
This commit is contained in:
Tim Eves 2019-01-17 17:12:42 +07:00
parent 4402a2b558
commit 5429037179
3 changed files with 44 additions and 37 deletions

View file

@ -32,7 +32,7 @@ namespace {
{
std::vector<km_kbp_context_item> res;
for (auto i = typename U::const_sentinal_iterator(text); i; ++i)
for (auto i = typename U::const_iterator(text); *i; ++i)
{
if(i.error()) return KM_KBP_STATUS_INVALID_UTF;
res.emplace_back(km_kbp_context_item {KM_KBP_CT_CHAR, {0,}, {*i}});
@ -65,7 +65,7 @@ namespace {
auto i = typename U::iterator(buf);
auto const e = decltype(i)(buf + buf_size - 1);
for (;i != e && ci->type != KM_KBP_CT_END; ++ci)
for (;i != e && ci->type != KM_KBP_CT_END && !i.error(); ++ci)
{
if (ci->type == KM_KBP_CT_CHAR)
{
@ -83,13 +83,13 @@ namespace {
else
{
auto n = 0;
typename U::codeunit_t cps[4/sizeof(typename U::codeunit_t)];
typename U::codeunit_t cps[4];
do
{
if (ci->type == KM_KBP_CT_CHAR)
{
int8_t l;
int8_t l = 4;
U::codec::put(cps, ci->character, l);
n += l;
}
@ -242,8 +242,8 @@ json & operator << (json & j, km::kbp::context const & ctxt) {
json & operator << (json & j, km_kbp_context_item const & i)
{
utf8::codeunit_t cps[5] = {0,};
int8_t l;
utf8::codeunit_t cps[4] = {0,};
int8_t l = 4;
switch (i.type)
{

View file

@ -11,6 +11,7 @@
*/
#pragma once
#include <algorithm>
#include <cstdint>
#include <cstddef>
#include <cstdlib>
@ -75,9 +76,11 @@ public:
if (usv < 0x10000) { l = 1; cp[0] = codeunit_t(usv); }
else
{
cp[0] = codeunit_t(lead_offset + (usv >> 10));
cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF));
l = 2;
l -= 2; if (l < 0) return;
// We have space so continue.
cp[0] = codeunit_t(lead_offset + (usv >> 10));
cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF));
l = 2;
}
}
@ -85,13 +88,12 @@ public:
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
const uint32_t uh = cp[0];
l = 1;
if (uh < 0xD800|| uh > 0xDFFF) { return uh; }
if (uh > 0xDBFF) { l = -1; return 0xFFFD; }
if (uh < 0xD800|| uh > 0xDFFF) { l = 1; return uh; }
if (uh > 0xDBFF || l < 2) { l = -1; return 0xFFFD; }
const uint32_t ul = cp[1];
if (ul < 0xDC00 || ul > 0xDFFF) { l = -1; return 0xFFFD; }
++l;
l = 2;
return (uh<<10) + ul + surrogate_offset;
}
@ -126,10 +128,10 @@ public:
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
if (usv < 0x80) {l = 1; cp[0] = usv; return; }
if (usv < 0x0800) {l = 2; cp[0] = 0xC0 + (usv >> 6); cp[1] = 0x80 + (usv & 0x3F); return; }
if (usv < 0x10000) {l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F); cp[2] = 0x80 + (usv & 0x3F); return; }
else {l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; }
--l; if (usv < 0x80) {l = 1; cp[0] = usv; return; }
--l; if (usv < 0x0800) {if (l < 0) return; l = 2; cp[0] = 0xC0 + (usv >> 6); cp[1] = 0x80 + (usv & 0x3F); return; };
--l; if (usv < 0x10000) {if (l < 0) return; l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F); cp[2] = 0x80 + (usv & 0x3F); return; }
else {if (--l < 0) return; l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; };
}
inline
@ -137,20 +139,21 @@ public:
{
const int8_t seq_sz = sz_lut[*cp >> 4];
uchar_t u = *cp & mask_lut[seq_sz];
l = 1;
bool toolong = false;
if (l < seq_sz) { l = -l; return 0xFFFD; }
auto rl = 1;
bool toolong = false;
switch(seq_sz) {
case 4: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong = (u < 0x10); fallthrough;
case 3: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x20); fallthrough;
case 2: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x80); fallthrough;
case 1: break;
case 4: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++rl; toolong = (u < 0x10); fallthrough;
case 3: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++rl; toolong |= (u < 0x20); fallthrough;
case 2: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++rl; toolong |= (u < 0x80); fallthrough;
case 1: l = seq_sz; break;
case 0: l = -1; return 0xFFFD;
}
if (l != seq_sz || toolong || u >= limit)
if (rl != seq_sz || toolong || u >= limit)
{
l = -l;
l = -rl;
return 0xFFFD;
}
return u;
@ -182,7 +185,8 @@ class _utf_iterator
typedef _utf_codec<sizeof(C)*8> codec;
C * cp;
mutable int8_t sl;
mutable int8_t sl,
sz;
public:
typedef C codeunit_type;
@ -195,18 +199,18 @@ public:
reference(const _utf_iterator & i): _i(i) {}
public:
operator value_type () const throw () { return codec::get(_i.cp, _i.sl); }
reference & operator = (const value_type usv) throw() { codec::put(_i.cp, usv, _i.sl); return *this; }
operator value_type () const throw () { return codec::get(_i.cp, _i.sl = _i.sz); }
reference & operator = (const value_type usv) throw() { codec::put(_i.cp, usv, _i.sl = _i.sz); return *this; }
friend class _utf_iterator;
};
_utf_iterator(const void * us=0) : cp(reinterpret_cast<C *>(const_cast<void *>(us))), sl(1) { }
_utf_iterator(const void * us=0) : cp(reinterpret_cast<C *>(const_cast<void *>(us))), sl(1), sz(127) { }
_utf_iterator & operator ++ () { cp += abs(sl); return *this; }
bool operator == (const _utf_iterator & rhs) const throw() { return cp >= rhs.cp; }
bool operator == (const _utf_iterator & rhs) const throw() { sz = std::min(ptrdiff_t(rhs.cp - cp),ptrdiff_t(127)); return 0 >= sz; }
bool operator != (const _utf_iterator & rhs) const throw() { return !operator==(rhs); }
reference operator * () const throw() { return *this; }
@ -245,9 +249,6 @@ struct utf
typedef _utf_iterator<C> iterator;
typedef _utf_iterator<const C> const_iterator;
typedef _utf_sentinal_iterator<C> sentinal_iterator;
typedef _utf_sentinal_iterator<const C> const_sentinal_iterator;
inline
static bool validate(codeunit_t * s, codeunit_t * e) throw() {
return _utf_codec<sizeof(C)*8>::validate(s,e);
@ -275,9 +276,9 @@ std::basic_string<T> convert(std::basic_string<F> const &src) {
using codeunit_t = typename utf<T>::codeunit_t;
auto r = std::basic_string<T>();
codeunit_t buf[4];
int8_t l = 1;
int8_t l = 4;
for (auto i = utf_const_iter(src.data()),
e = decltype(i)(src.data() + src.size()); i != e && l > 0; ++i) {
e = decltype(i)(src.data() + src.size()); i != e && l > 0; ++i, l = 4) {
utf<T>::codec::put(buf, uchar_t(*i), l);
r.append(reinterpret_cast<T *>(&buf[0]), l);
}

View file

@ -70,9 +70,15 @@ int main(int, char * [])
try_status(km_kbp_context_items_to_utf16(ctxt2, ctxt_buffer, &ctxt_size));
if (initial_smp_context != ctxt_buffer) return __LINE__;
// Test buffer overrun protection.
ctxt_size=smp_ctxt_size/3;
ctxt_size=4; // This includes space for the null terminator
if (km_kbp_context_items_to_utf16(ctxt2, ctxt_buffer, &ctxt_size)
!= KM_KBP_STATUS_INSUFFICENT_BUFFER)
!= KM_KBP_STATUS_INSUFFICENT_BUFFER
|| ctxt_size != 4)
return __LINE__;
ctxt_size=8; // This includes space for the null terminator
if (km_kbp_context_items_to_utf8(ctxt4, ctxt_u8_buffer, &ctxt_size)
!= KM_KBP_STATUS_INSUFFICENT_BUFFER
|| ctxt_size != 6)
return __LINE__;
// Create a mock context object and set the items