mariadb/storage/connect/plugutil.cpp
Mikhail Chalov 2ff01e763e Fix insecure use of strcpy, strcat and sprintf in Connect
Old style C functions `strcpy()`, `strcat()` and `sprintf()` are vulnerable to
security issues due to lacking memory boundary checks. Replace these in the
Connect storage engine with safe new and/or custom functions such as
`snprintf()` `safe_strcpy()` and `safe_strcat()`.

With this change FlawFinder and other static security analyzers report 287
fewer findings.

All new code of the whole pull request, including one or several files that are
either new files or modified ones, are contributed under the BSD-new license. I
am contributing on behalf of my employer Amazon Web Services, Inc.
2023-05-12 15:37:00 +01:00

646 lines
21 KiB
C++

/************** PlugUtil C Program Source Code File (.C) ***************/
/* */
/* PROGRAM NAME: PLUGUTIL */
/* ------------- */
/* Version 3.1 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 1993-2020 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* This program are initialization and utility Plug routines. */
/* */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
/* -------------------------------------- */
/* */
/* REQUIRED FILES: */
/* --------------- */
/* See Readme.C for a list and description of required SYSTEM files. */
/* */
/* PLUG.C - Source code */
/* GLOBAL.H - Global declaration file */
/* OPTION.H - Option declaration file */
/* */
/* REQUIRED LIBRARIES: */
/* ------------------- */
/* */
/* OS2.LIB - OS2 libray */
/* LLIBCE.LIB - Protect mode/standard combined large model C */
/* library */
/* */
/* REQUIRED PROGRAMS: */
/* ------------------ */
/* */
/* IBM C Compiler */
/* IBM Linker */
/* */
/***********************************************************************/
/***********************************************************************/
/* */
/* Include relevant MariaDB header file. */
/* */
/***********************************************************************/
#include "my_global.h"
#if defined(_WIN32)
//#include <windows.h>
#else
#if defined(UNIX) || defined(UNIV_LINUX)
#include <errno.h>
#include <unistd.h>
//#define __stdcall
#else
#include <dir.h>
#endif
#include <stdarg.h>
#endif
#if defined(WIN)
#include <alloc.h>
#endif
#include <errno.h> /* definitions of ERANGE ENOMEM */
#if !defined(UNIX) && !defined(UNIV_LINUX)
#include <direct.h> /* Directory management library */
#endif
/***********************************************************************/
/* */
/* Include application header files */
/* */
/* global.h is header containing all global declarations. */
/* */
/***********************************************************************/
#define STORAGE /* Initialize global variables */
#include "m_string.h"
#include "osutil.h"
#include "global.h"
#include "plgdbsem.h"
#if defined(NEWMSG)
#include "rcmsg.h"
#endif // NEWMSG
#if defined(_WIN32)
extern HINSTANCE s_hModule; /* Saved module handle */
#endif // _WIN32
#if defined(XMSG)
extern char *msg_path;
char *msglang(void);
#endif // XMSG
/***********************************************************************/
/* Local Definitions and static variables */
/***********************************************************************/
typedef struct {
ushort Segsize;
ushort Size;
} AREASIZE;
ACTIVITY defActivity = { /* Describes activity and language */
NULL, /* Points to user work area(s) */
"Unknown"}; /* Application name */
#if defined(XMSG) || defined(NEWMSG)
static char stmsg[200];
#endif // XMSG || NEWMSG
#if defined(UNIX) || defined(UNIV_LINUX)
#include "rcmsg.h"
#endif // UNIX
/**************************************************************************/
/* Conditional tracing output function. */
/**************************************************************************/
void xtrc(uint x, char const *fmt, ...)
{
if (GetTraceValue() & x) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
} // endif x
} // end of xtrc
/**************************************************************************/
/* Tracing output function. */
/**************************************************************************/
void htrc(char const* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
} // end of htrc
/***********************************************************************/
/* Plug initialization routine. */
/* Language points on initial language name and eventual path. */
/* Return value is the pointer to the Global structure. */
/***********************************************************************/
PGLOBAL PlugInit(LPCSTR Language, size_t worksize)
{
PGLOBAL g;
if (trace(2))
htrc("PlugInit: Language='%-.256s'\n",
((!Language) ? "Null" : (char*)Language));
try {
g = new GLOBAL;
} catch (...) {
fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
return NULL;
} // end try/catch
g->Sarea = NULL;
g->Createas = false;
g->Alchecked = 0;
g->Mrr = 0;
g->Activityp = NULL;
g->Xchk = NULL;
g->N = 0;
g->More = 0;
g->Saved_Size = 0;
strcpy(g->Message, "");
/*******************************************************************/
/* Allocate the main work segment. */
/*******************************************************************/
if (worksize && AllocSarea(g, worksize)) {
char errmsg[MAX_STR];
snprintf(errmsg, sizeof(errmsg) - 1, MSG(WORK_AREA), g->Message);
strcpy(g->Message, errmsg);
} // endif Sarea
g->jump_level = -1; /* New setting to allow recursive call of Plug */
return(g);
} /* end of PlugInit */
/***********************************************************************/
/* PlugExit: Terminate Plug operations. */
/***********************************************************************/
PGLOBAL PlugExit(PGLOBAL g)
{
if (g) {
PDBUSER dup = PlgGetUser(g);
if (dup)
free(dup);
FreeSarea(g);
delete g;
} // endif g
return NULL;
} // end of PlugExit
/***********************************************************************/
/* Remove the file type from a file name. */
/* Note: this routine is not really implemented for Unix. */
/***********************************************************************/
LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName)
{
#if defined(_WIN32)
char drive[_MAX_DRIVE];
#else
char *drive = NULL;
#endif
char direc[_MAX_DIR];
char fname[_MAX_FNAME];
char ftype[_MAX_EXT];
_splitpath(FileName, drive, direc, fname, ftype);
if (trace(2)) {
htrc("after _splitpath: FileName=%-.256s\n", FileName);
htrc("drive=%-.256s dir=%-.256s fname=%-.256s ext=%-.256s\n",
SVP(drive), direc, fname, ftype);
} // endif trace
_makepath(pBuff, drive, direc, fname, "");
if (trace(2))
htrc("buff='%-.256s'\n", pBuff);
return pBuff;
} // end of PlugRemoveType
BOOL PlugIsAbsolutePath(LPCSTR path)
{
#if defined(_WIN32)
return ((path[0] >= 'a' && path[0] <= 'z') ||
(path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':';
#else
return path[0] == '/';
#endif
}
/***********************************************************************/
/* Set the full path of a file relatively to a given path. */
/* Note: this routine is not really implemented for Unix. */
/***********************************************************************/
LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
{
char newname[_MAX_PATH];
char direc[_MAX_DIR], defdir[_MAX_DIR], tmpdir[_MAX_DIR];
char fname[_MAX_FNAME];
char ftype[_MAX_EXT];
#if defined(_WIN32)
char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE];
#else
char *drive = NULL, *defdrv = NULL;
#endif
if (trace(2))
htrc("prefix=%-.256s fn=%-.256s path=%-.256s\n", prefix, FileName, defpath);
if (strlen(FileName) >= _MAX_PATH)
{
*pBuff= 0; /* Hope this is treated as error of some kind*/
return FileName;
}
if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
strcpy(pBuff, FileName); // Remote file
return pBuff;
} // endif
if (PlugIsAbsolutePath(FileName))
{
strcpy(pBuff, FileName); // FileName includes absolute path
return pBuff;
} // endif
#if !defined(_WIN32)
if (*FileName == '~') {
if (_fullpath(pBuff, FileName, _MAX_PATH)) {
if (trace(2))
htrc("pbuff='%-.256s'\n", pBuff);
return pBuff;
} else
return FileName; // Error, return unchanged name
} // endif FileName
#endif // !_WIN32
if (prefix && strcmp(prefix, ".") && !PlugIsAbsolutePath(defpath))
{
char tmp[_MAX_PATH];
int len= snprintf(tmp, sizeof(tmp) - 1, "%s%s%s",
prefix, defpath, FileName);
memcpy(pBuff, tmp, (size_t) len);
pBuff[len]= '\0';
return pBuff;
}
_splitpath(FileName, drive, direc, fname, ftype);
if (defpath) {
char c = defpath[strlen(defpath) - 1];
strcpy(tmpdir, defpath);
if (c != '/' && c != '\\')
strcat(tmpdir, "/");
} else
strcpy(tmpdir, "./");
_splitpath(tmpdir, defdrv, defdir, NULL, NULL);
if (trace(2)) {
htrc("after _splitpath: FileName=%-.256s\n", FileName);
#if defined(_WIN32)
htrc("drive=%-.256s dir=%-.256s fname=%-.256s ext=%-.256s\n", drive, direc, fname, ftype);
htrc("defdrv=%-.256s defdir=%-.256s\n", defdrv, defdir);
#else
htrc("dir=%-.256s fname=%-.256s ext=%-.256s\n", direc, fname, ftype);
#endif
} // endif trace
if (drive && !*drive)
strcpy(drive, defdrv);
switch (*direc) {
case '\0':
snprintf(direc, sizeof(direc), "%s", defdir);
break;
case '\\':
case '/':
break;
default:
// This supposes that defdir ends with a SLASH
safe_strcat(defdir, sizeof(defdir), direc);
snprintf(direc, sizeof(direc), "%s", defdir);
} // endswitch
_makepath(newname, drive, direc, fname, ftype);
if (trace(2))
htrc("newname='%-.256s'\n", newname);
if (_fullpath(pBuff, newname, _MAX_PATH)) {
if (trace(2))
htrc("pbuff='%-.256s'\n", pBuff);
return pBuff;
} else
return FileName; // Error, return unchanged name
} // end of PlugSetPath
#if defined(XMSG)
/***********************************************************************/
/* PlugGetMessage: get a message from the message file. */
/***********************************************************************/
char *PlugReadMessage(PGLOBAL g, int mid, char *m)
{
char msgfile[_MAX_PATH], msgid[32], buff[256];
char *msg;
FILE *mfile = NULL;
//GetPrivateProfileString("Message", msglang, "Message\\english.msg",
// msgfile, _MAX_PATH, plgini);
//strcat(strcat(strcpy(msgfile, msg_path), msglang()), ".msg");
strcat(strcpy(buff, msglang()), ".msg");
PlugSetPath(msgfile, NULL, buff, msg_path);
if (!(mfile = fopen(msgfile, "rt"))) {
snprintf(stmsg, sizeof(stmsg), "Fail to open message file %s", msgfile);
goto err;
} // endif mfile
for (;;)
if (!fgets(buff, 256, mfile)) {
snprintf(stmsg, sizeof(stmsg), "Cannot get message %d %s", mid, SVP(m));
goto fin;
} else
if (atoi(buff) == mid)
break;
if (sscanf(buff, " %*d %.31s \"%.255[^\"]", msgid, stmsg) < 2) {
// Old message file
if (!sscanf(buff, " %*d \"%.255[^\"]", stmsg)) {
snprintf(stmsg, sizeof(stmsg), "Bad message file for %d %s", mid, SVP(m));
goto fin;
} else
m = NULL;
} // endif sscanf
if (m && strcmp(m, msgid)) {
// Message file is out of date
strcpy(stmsg, m);
goto fin;
} // endif m
fin:
fclose(mfile);
err:
if (g) {
// Called by STEP
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
return msg;
} // end of PlugReadMessage
#elif defined(NEWMSG)
/***********************************************************************/
/* PlugGetMessage: get a message from the resource string table. */
/***********************************************************************/
char *PlugGetMessage(PGLOBAL g, int mid)
{
char *msg;
#if 0 // was !defined(UNIX) && !defined(UNIV_LINUX)
int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200);
if (n == 0) {
DWORD rc = GetLastError();
const int BUF_SIZE= 512;
msg = (char*)PlugSubAlloc(g, NULL, BUF_SIZE); // Extend buf allocation
n = snprintf(msg, BUF_SIZE, "Message %d, rc=%d: ", mid, rc);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)(msg + n), BUF_SIZE - n, NULL);
return msg;
} // endif n
#else // ALL
if (!GetRcString(mid, stmsg, 200))
snprintf(stmsg, sizeof(stmsg) "Message %d not found", mid);
#endif // ALL
if (g) {
// Called by STEP
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
return msg;
} // end of PlugGetMessage
#endif // NEWMSG
#if defined(_WIN32)
/***********************************************************************/
/* Return the line length of the console screen buffer. */
/***********************************************************************/
short GetLineLength(PGLOBAL g)
{
CONSOLE_SCREEN_BUFFER_INFO coninfo;
HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE);
BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo);
return (b) ? coninfo.dwSize.X : 0;
} // end of GetLineLength
#endif // _WIN32
/***********************************************************************/
/* Program for memory allocation of work and language areas. */
/***********************************************************************/
bool AllocSarea(PGLOBAL g, size_t size)
{
/*********************************************************************/
/* This is the allocation routine for the WIN32/UNIX/AIX version. */
/*********************************************************************/
#if defined(_WIN32)
if (size >= 1048576) // 1M
g->Sarea = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
else
#endif
g->Sarea = malloc(size);
if (!g->Sarea) {
snprintf(g->Message, sizeof(g->Message), MSG(MALLOC_ERROR), "malloc");
g->Sarea_Size = 0;
} else {
g->Sarea_Size = size;
PlugSubSet(g->Sarea, size);
} // endif Sarea
#if defined(DEVELOPMENT)
if (true) {
#else
if (trace(8)) {
#endif
if (g->Sarea) {
htrc("Work area of %zd allocated at %p\n", size, g->Sarea);
} else
htrc("SareaAlloc: %-.256s\n", g->Message);
} // endif trace
return (!g->Sarea);
} // end of AllocSarea
/***********************************************************************/
/* Program for memory freeing the work area. */
/***********************************************************************/
void FreeSarea(PGLOBAL g)
{
if (g->Sarea) {
#if defined(_WIN32)
if (g->Sarea_Size >= 1048576) // 1M
VirtualFree(g->Sarea, 0, MEM_RELEASE);
else
#endif
free(g->Sarea);
#if defined(DEVELOPMENT)
if (true)
#else
if (trace(8))
#endif
htrc("Freeing Sarea at %p size = %zd\n", g->Sarea, g->Sarea_Size);
g->Sarea = NULL;
g->Sarea_Size = 0;
} // endif Sarea
return;
} // end of FreeSarea
/***********************************************************************/
/* Program for SubSet initialization of memory pools. */
/* Here there should be some verification done such as validity of */
/* the address and size not larger than memory size. */
/***********************************************************************/
BOOL PlugSubSet(void *memp, size_t size)
{
PPOOLHEADER pph = (PPOOLHEADER)memp;
pph->To_Free = (size_t)sizeof(POOLHEADER);
pph->FreeBlk = size - pph->To_Free;
return FALSE;
} /* end of PlugSubSet */
/***********************************************************************/
/* Use it to export a function that do throwing. */
/***********************************************************************/
static void *DoThrow(int n)
{
throw n;
} /* end of DoThrow */
/***********************************************************************/
/* Program for sub-allocating one item in a storage area. */
/* The simple way things are done here is based on the fact */
/* that no freeing of suballocated blocks is permitted in CONNECT. */
/***********************************************************************/
void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
{
PPOOLHEADER pph; /* Points on area header. */
if (!memp)
/*******************************************************************/
/* Allocation is to be done in the Sarea. */
/*******************************************************************/
memp = g->Sarea;
size = ROUNDUP_TO_8(size); /* Round up size to multiple of 8 */
pph = (PPOOLHEADER)memp;
if (trace(16))
htrc("SubAlloc in %p size=%zd used=%zd free=%zd\n",
memp, size, pph->To_Free, pph->FreeBlk);
if (size > pph->FreeBlk) { /* Not enough memory left in pool */
PCSZ pname = "Work";
snprintf(g->Message, sizeof(g->Message),
"Not enough memory in %-.256s area for request of %zu (used=%zu free=%zu)",
pname, size, pph->To_Free, pph->FreeBlk);
if (trace(1))
htrc("PlugSubAlloc: %-.256s\n", g->Message);
DoThrow(1234);
} /* endif size OS32 code */
/*********************************************************************/
/* Do the suballocation the simplest way. */
/*********************************************************************/
memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */
pph->To_Free += size; /* New offset of pool free block */
pph->FreeBlk -= size; /* New size of pool free block */
if (trace(16))
htrc("Done memp=%p used=%zd free=%zd\n",
memp, pph->To_Free, pph->FreeBlk);
return (memp);
} /* end of PlugSubAlloc */
/***********************************************************************/
/* Program for sub-allocating and copying a string in a storage area. */
/***********************************************************************/
char *PlugDup(PGLOBAL g, const char *str)
{
if (str) {
char *sm = (char*)PlugSubAlloc(g, NULL, strlen(str) + 1);
snprintf(sm, strlen(str) + 1, "%s", str);
return sm;
} else
return NULL;
} // end of PlugDup
/*************************************************************************/
/* This routine makes a pointer from an offset to a memory pointer. */
/*************************************************************************/
void* MakePtr(void* memp, size_t offset)
{
// return ((offset == 0) ? NULL : &((char*)memp)[offset]);
return (!offset) ? NULL : (char *)memp + offset;
} /* end of MakePtr */
/*************************************************************************/
/* This routine makes an offset from a pointer new format. */
/*************************************************************************/
size_t MakeOff(void* memp, void* ptr)
{
if (ptr) {
#if defined(_DEBUG) || defined(DEVELOPMENT)
if (ptr <= memp) {
fprintf(stderr, "ptr %p <= memp %p", ptr, memp);
DoThrow(999);
} // endif ptr
#endif // _DEBUG || DEVELOPMENT
return (size_t)(((char*)ptr) - ((char*)memp));
} else
return 0;
} /* end of MakeOff */
/*---------------------- End of PLUGUTIL program ------------------------*/