1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-04-18 21:44:02 +03:00
Sergey Zefirov 0a2e9760ee
Fix for JSON_VALUE function to remove OOB stack access (#2852)
MCOL-271 introduced a bug in JSON_VALUE that was discovered during
implementation of ASAN builds. The changes here restore normal
functionality.

In short, changes in MCOL-271 introduced a local variable instead of
reference to a string in ConstantColumn's fResult.strVal. The handling
of ConstantColumn is different because ConstantColumn's value is used
to initialize JSON path once. JSON path value holds pointer to data it
does not own and if there are two or more rows the data can be corrupted
and/or be out of stack bounds.

The changes here introduce reference to a NullString that is held in the
ConstantColumn's fResult.strVal and uses appropriate functions to obtain
data from the NullString. CC's fResult is held by CC and strVal is also
neither changing nor moving during operation, which allow JSON path to
hold correct pointers during multi-row operation.
2023-05-31 15:30:40 +03:00

123 lines
2.8 KiB
C++

#include "functor_json.h"
#include "functioncolumn.h"
#include "constantcolumn.h"
using namespace execplan;
#include "rowgroup.h"
using namespace rowgroup;
#include "joblisttypes.h"
using namespace joblist;
#include "jsonhelpers.h"
using namespace funcexp::helpers;
namespace funcexp
{
bool JSONEgWrapper::checkAndGetScalar(string& ret, int* error)
{
CHARSET_INFO* cs;
const uchar* js;
uint jsLen;
if (!json_value_scalar(this))
{
/* We only look for scalar values! */
if (json_skip_level(this) || json_scan_next(this))
*error = 1;
return true;
}
if (value_type == JSON_VALUE_TRUE || value_type == JSON_VALUE_FALSE)
{
cs = &my_charset_utf8mb4_bin;
js = (const uchar*)((value_type == JSON_VALUE_TRUE) ? "1" : "0");
jsLen = 1;
}
else
{
cs = s.cs;
js = value;
jsLen = value_len;
}
int strLen = jsLen * cs->mbmaxlen;
char* buf = (char*)alloca(jsLen + strLen);
if ((strLen = json_unescape(cs, js, js + jsLen, cs, (uchar*)buf, (uchar*)buf + jsLen + strLen)) > 0)
{
buf[strLen] = '\0';
ret.append(buf);
return 0;
}
return strLen;
}
/*
Returns NULL, not an error if the found value
is not a scalar.
*/
bool JSONPathWrapper::extract(std::string& ret, rowgroup::Row& row, execplan::SPTP& funcParamJS,
execplan::SPTP& funcParamPath)
{
bool isNullJS = false, isNullPath = false;
const string js = funcParamJS->data()->getStrVal(row, isNullJS).safeString("");
const utils::NullString& sjsp = funcParamPath->data()->getStrVal(row, isNullPath);
if (isNullJS || isNullPath)
return true;
int error = 0;
if (!parsed)
{
if (!constant)
{
ConstantColumn* constCol = dynamic_cast<ConstantColumn*>(funcParamPath->data());
constant = (constCol != nullptr);
}
if (isNullPath || json_path_setup(&p, getCharset(funcParamPath), (const uchar*)sjsp.str(),
(const uchar*)sjsp.end()))
return true;
parsed = constant;
}
JSONEgWrapper je(js, getCharset(funcParamJS));
currStep = p.steps;
do
{
if (error)
return true;
IntType arrayCounters[JSON_DEPTH_LIMIT];
if (json_find_path(&je, &p, &currStep, arrayCounters))
return true;
if (json_read_value(&je))
return true;
} while (unlikely(checkAndGetValue(&je, ret, &error)));
return false;
}
CalpontSystemCatalog::ColType Func_json_value::operationType(FunctionParm& fp,
CalpontSystemCatalog::ColType& resultType)
{
return fp[0]->data()->resultType();
}
string Func_json_value::getStrVal(rowgroup::Row& row, FunctionParm& fp, bool& isNull,
execplan::CalpontSystemCatalog::ColType& type)
{
string ret;
isNull = JSONPathWrapper::extract(ret, row, fp[0], fp[1]);
return isNull ? "" : ret;
}
} // namespace funcexp