mirror of
https://github.com/DynamoRIO/dynamorio.git
synced 2026-10-12 02:02:04 +00:00
Updates all the "FIXME" comments in the repository to instead use "TODO" (for those about missing implementations: "NYI" or "ASSERT_NOT_IMPLEMENTED") or "XXX" (everything else). This matches our style guide which discourages "FIXME" as its connotations are too negative and too easily mis-interpreted by readers and in code audits to imply that the current state of things is broken in some way instead of just meaning something could be improved.
411 lines
12 KiB
C
411 lines
12 KiB
C
/* **********************************************************
|
|
* Copyright (c) 2019-2025 Google, Inc. All rights reserved.
|
|
* Copyright (c) 2005-2006 VMware, Inc. All rights reserved.
|
|
* **********************************************************/
|
|
|
|
/*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of VMware, Inc. nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
* DAMAGE.
|
|
*/
|
|
|
|
#include "share.h"
|
|
#include "parser.h"
|
|
#include <stdio.h>
|
|
|
|
#ifndef UNIT_TEST
|
|
|
|
char *
|
|
next_token_sep(char *start, SIZE_T *len, char sep)
|
|
{
|
|
char *curtok = start;
|
|
char seps[16];
|
|
_snprintf(seps, 16, "%c\r\n", sep);
|
|
|
|
DO_ASSERT(start != NULL);
|
|
DO_ASSERT(len != NULL);
|
|
|
|
*len = 0;
|
|
|
|
if (start == NULL)
|
|
return NULL;
|
|
|
|
/* special case: '=' followed by a newline */
|
|
if (start[0] == sep && (start[1] == '\r' || start[1] == '\n')) {
|
|
curtok += 1;
|
|
return curtok;
|
|
}
|
|
|
|
/* determine the next token */
|
|
while (curtok[0] != '\0' && *len == 0) {
|
|
*len = strcspn(curtok, seps);
|
|
if (*len == 0)
|
|
curtok = curtok + strspn(curtok, seps);
|
|
}
|
|
|
|
return curtok;
|
|
}
|
|
|
|
char *
|
|
next_token(char *start, SIZE_T *len)
|
|
{
|
|
return next_token_sep(start, len, '=');
|
|
}
|
|
|
|
/* assumes that start points directly after the start_delimeter
|
|
* returns the size of the message block, or -1 on error */
|
|
char *
|
|
get_message_block_size(char *start, WCHAR *end_delimiter_w, SIZE_T *size)
|
|
{
|
|
char *endptr;
|
|
char end_delimiter[MAX_PATH];
|
|
|
|
DO_ASSERT(start != NULL);
|
|
DO_ASSERT(end_delimiter_w != NULL);
|
|
DO_ASSERT(size != NULL);
|
|
|
|
_snprintf(end_delimiter, MAX_PATH, "%S", end_delimiter_w);
|
|
endptr = strstr(start, end_delimiter);
|
|
|
|
if (endptr == NULL) {
|
|
DO_DEBUG(DL_WARN, printf("No %s end delimiter!\n", end_delimiter););
|
|
*size = 0xffffffff;
|
|
return NULL;
|
|
} else {
|
|
DO_DEBUG(DL_VERB,
|
|
printf("Block size %zd for %s:\n", endptr - start, end_delimiter););
|
|
*size = endptr - start;
|
|
return endptr + strlen(end_delimiter);
|
|
}
|
|
}
|
|
|
|
/* takes an optional separator, by default '=' */
|
|
char *
|
|
parse_line_sep(char *start, char sep, BOOL *done, WCHAR *param, WCHAR *value,
|
|
SIZE_T maxchars)
|
|
{
|
|
char *curtok;
|
|
SIZE_T toklen = 0;
|
|
const WCHAR *prefix;
|
|
|
|
DO_ASSERT(start != NULL);
|
|
DO_ASSERT(done != NULL);
|
|
DO_ASSERT(param != NULL);
|
|
DO_ASSERT(maxchars > 0);
|
|
|
|
*done = FALSE;
|
|
param[0] = L'\0';
|
|
value[0] = L'\0';
|
|
|
|
curtok = start;
|
|
|
|
curtok = next_token_sep(start, &toklen, sep);
|
|
|
|
DO_DEBUG(DL_FINEST, printf("curtok (tl=%zu):\n%s", toklen, curtok););
|
|
|
|
if (toklen == 0 && curtok[0] == '\0') {
|
|
*done = TRUE;
|
|
goto parse_line_out;
|
|
}
|
|
|
|
_snwprintf(param, MIN(toklen, maxchars - 1), L"%S", curtok);
|
|
param[MIN(toklen, maxchars - 1)] = L'\0';
|
|
|
|
DO_DEBUG(DL_FINEST,
|
|
printf("curtok: %s, nc=%d (%d or %d?)\n", curtok, curtok[toklen + 1], '\r',
|
|
'\n'););
|
|
|
|
/* handle the empty option properly */
|
|
if (curtok[toklen] == '\r' || curtok[toklen] == '\n') {
|
|
value[0] = L'\0';
|
|
} else {
|
|
curtok = next_token_sep(curtok + toklen, &toklen, sep);
|
|
|
|
if (toklen == 0 && curtok[0] == '\0') {
|
|
*done = TRUE;
|
|
goto parse_line_out;
|
|
}
|
|
|
|
DO_DEBUG(DL_FINEST, printf("vt(%zu): %s\n", toklen, curtok););
|
|
|
|
/* all values that start with \\ have DR_HOME prepended */
|
|
if ('\\' == curtok[0])
|
|
prefix = get_dynamorio_home();
|
|
else
|
|
prefix = L"";
|
|
|
|
_snwprintf(value, MIN(toklen + wcslen(prefix), maxchars - 1), L"%s%S", prefix,
|
|
curtok);
|
|
value[MIN(toklen + wcslen(prefix), maxchars - 1)] = L'\0';
|
|
}
|
|
|
|
parse_line_out:
|
|
|
|
DO_DEBUG(DL_FINEST, printf("parsed line: %S(%zu)=%S\n", param, toklen, value););
|
|
|
|
return curtok + toklen;
|
|
}
|
|
|
|
/* first argument is where to start searching from.
|
|
* returns a value which is the right place to start searching
|
|
* from for the next token.
|
|
* a line is either a single token, or an NVP (separated by =). */
|
|
char *
|
|
parse_line(char *start, BOOL *done, WCHAR *param, WCHAR *value, SIZE_T maxchars)
|
|
{
|
|
return parse_line_sep(start, '=', done, param, value, maxchars);
|
|
}
|
|
|
|
void
|
|
msg_append(char *msg_buffer, SIZE_T maxchars, WCHAR *data, SIZE_T *accumlen)
|
|
{
|
|
DO_ASSERT(accumlen != NULL);
|
|
|
|
if (data == NULL)
|
|
return;
|
|
|
|
if (msg_buffer != NULL && strlen(msg_buffer) + wcslen(data) < maxchars) {
|
|
SIZE_T oldsz = strlen(msg_buffer);
|
|
DO_DEBUG(DL_FINEST,
|
|
printf("msg_buffer: %s, os=%zu, data=%S\n", msg_buffer, oldsz, data););
|
|
_snprintf(msg_buffer + oldsz, maxchars - oldsz, "%S", data);
|
|
msg_buffer[oldsz + wcslen(data)] = '\0';
|
|
DO_DEBUG(DL_FINEST, printf("msg_buffer: %s\n", msg_buffer););
|
|
DO_ASSERT(strlen(msg_buffer) == oldsz + wcslen(data));
|
|
}
|
|
|
|
*accumlen += wcslen(data);
|
|
return;
|
|
}
|
|
|
|
/* ok this is annoying.
|
|
*
|
|
* apparently there's a bug in MS's CRT that causes \r\n to get
|
|
* converted to \r\r\n.
|
|
*
|
|
* see the unit tests below for an example of this bug.
|
|
*
|
|
* i guess we're ok with \r\r\n since notepad handles it fine. that's
|
|
* preferable to just \n which notepad can't handle.
|
|
*/
|
|
|
|
void
|
|
msg_append_nvp(char *msg_buffer, SIZE_T maxchars, SIZE_T *accumlen, WCHAR *name,
|
|
WCHAR *value)
|
|
{
|
|
/* exclude installation-specific parameters */
|
|
if (0 == wcscmp(name, L_DYNAMORIO_VAR_HOME) ||
|
|
0 == wcscmp(name, L_DYNAMORIO_VAR_LOGDIR))
|
|
return;
|
|
|
|
msg_append(msg_buffer, maxchars, name, accumlen);
|
|
msg_append(msg_buffer, maxchars, L_EQUALS, accumlen);
|
|
|
|
/* relativize any paths to DRHOME */
|
|
if (0 == wcsncmp(value, get_dynamorio_home(), wcslen(get_dynamorio_home())))
|
|
msg_append(msg_buffer, maxchars, value + wcslen(get_dynamorio_home()), accumlen);
|
|
else
|
|
msg_append(msg_buffer, maxchars, value, accumlen);
|
|
|
|
msg_append(msg_buffer, maxchars, L_NEWLINE, accumlen);
|
|
}
|
|
|
|
#else // ifdef UNIT_TEST
|
|
|
|
int
|
|
main()
|
|
{
|
|
char *testline = "GLOBAL_PROTECT=1\r\nBEGIN_BLOCK\r\nAPP_NAME=inetinfo."
|
|
"exe\r\nDYNAMORIO_OPTIONS=\r\nFOO=\\bar.dll\r\n";
|
|
WCHAR *sample = L"sample.mfp";
|
|
SIZE_T len;
|
|
DWORD res;
|
|
char *policy;
|
|
|
|
set_debuglevel(DL_INFO);
|
|
set_abortlevel(DL_WARN);
|
|
|
|
/* load the sample policy file for testing */
|
|
res = read_file_contents(sample, NULL, 0, &len);
|
|
DO_ASSERT(res == ERROR_MORE_DATA);
|
|
DO_ASSERT(len > 1000);
|
|
|
|
policy = (char *)malloc(len);
|
|
res = read_file_contents(sample, policy, len, NULL);
|
|
DO_ASSERT(res == ERROR_SUCCESS);
|
|
|
|
/* next_token tests */
|
|
{
|
|
char *tok, *line, *ptr;
|
|
UINT len;
|
|
|
|
line = testline;
|
|
tok = next_token(line, &len);
|
|
|
|
DO_ASSERT(line == tok);
|
|
DO_ASSERT(len == strlen("GLOBAL_PROTECT"));
|
|
|
|
ptr = line + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(ptr + 1 == tok);
|
|
DO_ASSERT(len == 1);
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(0 == strncmp(tok, "BEGIN_BLOCK", strlen("BEGIN_BLOCK")));
|
|
DO_ASSERT(len == strlen("BEGIN_BLOCK"));
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(0 == strncmp(tok, "APP_NAME", strlen("APP_NAME")));
|
|
DO_ASSERT(len == strlen("APP_NAME"));
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(0 == strncmp(tok, "inetinfo.exe", strlen("inetinfo.exe")));
|
|
DO_ASSERT(len == strlen("inetinfo.exe"));
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(0 == strncmp(tok, "DYNAMORIO_OPTIONS", strlen("DYNAMORIO_OPTIONS")));
|
|
DO_ASSERT(len == strlen("DYNAMORIO_OPTIONS"));
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(len == 0);
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(0 == strncmp(tok, "FOO", strlen("FOO")));
|
|
DO_ASSERT(len == strlen("FOO"));
|
|
|
|
ptr = tok + len;
|
|
tok = next_token(ptr, &len);
|
|
DO_ASSERT(len == strlen("\\bar.dll"));
|
|
|
|
/* XXX: long string / overrun tets.. */
|
|
}
|
|
|
|
/* get_message_block_size tests */
|
|
{
|
|
char *line, *ptr;
|
|
UINT len;
|
|
|
|
line = "a b\r\nfoo bar=blah\r\nEND_TEST_BLOCK\r\n";
|
|
ptr = get_message_block_size(line, L"END_TEST_BLOCK", &len);
|
|
DO_ASSERT(len == 19);
|
|
DO_ASSERT(ptr == line + strlen(line) - 2);
|
|
}
|
|
|
|
/* parse_line tests */
|
|
{
|
|
WCHAR param[MAX_PATH], value[MAX_PATH];
|
|
BOOL done;
|
|
char *ptr, *line = testline;
|
|
|
|
ptr = parse_line(line, &done, param, value, MAX_PATH);
|
|
DO_ASSERT(!done);
|
|
DO_ASSERT_WSTR_EQ(param, L"GLOBAL_PROTECT");
|
|
DO_ASSERT_WSTR_EQ(value, L"1");
|
|
|
|
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
|
|
DO_ASSERT(!done);
|
|
DO_ASSERT_WSTR_EQ(param, L"BEGIN_BLOCK");
|
|
DO_ASSERT_WSTR_EQ(value, L"");
|
|
|
|
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
|
|
DO_ASSERT(!done);
|
|
DO_ASSERT_WSTR_EQ(param, L"APP_NAME");
|
|
DO_ASSERT_WSTR_EQ(value, L"inetinfo.exe");
|
|
|
|
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
|
|
DO_ASSERT(!done);
|
|
DO_ASSERT_WSTR_EQ(param, L"DYNAMORIO_OPTIONS");
|
|
DO_ASSERT_WSTR_EQ(value, L"");
|
|
|
|
ptr = parse_line(ptr, &done, param, value, MAX_PATH);
|
|
DO_ASSERT(!done);
|
|
DO_ASSERT_WSTR_EQ(param, L"FOO");
|
|
DO_ASSERT(NULL != wcsstr(value, L"bar"));
|
|
DO_ASSERT(NULL != wcsstr(value, get_dynamorio_home()));
|
|
}
|
|
|
|
/* msg_append tests */
|
|
{
|
|
char buf[MAX_PATH];
|
|
UINT loc;
|
|
buf[0] = '\0';
|
|
|
|
loc = 0;
|
|
msg_append(buf, 0, L"GLOBAL", &loc);
|
|
DO_ASSERT(0 == strlen(buf));
|
|
DO_ASSERT(6 == loc);
|
|
|
|
loc = 0;
|
|
msg_append(buf, MAX_PATH, L"GLOBAL", &loc);
|
|
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
|
|
DO_ASSERT(6 == loc);
|
|
|
|
loc = 6;
|
|
msg_append(buf, 0, L"_PROTECT", &loc);
|
|
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
|
|
DO_ASSERT(14 == loc);
|
|
|
|
loc = 6;
|
|
msg_append(buf, 6, L"_PROTECT", &loc);
|
|
DO_ASSERT(0 == strcmp(buf, "GLOBAL"));
|
|
DO_ASSERT(14 == loc);
|
|
|
|
loc = 6;
|
|
msg_append(buf, MAX_PATH, L"_PROTECT", &loc);
|
|
DO_ASSERT(0 == strcmp(buf, "GLOBAL_PROTECT"));
|
|
DO_ASSERT(14 == loc);
|
|
}
|
|
|
|
/* MSVCRT \r\r\n bug */
|
|
{
|
|
WCHAR *outfn = L"testcr.txt";
|
|
char *str = "line\r\n";
|
|
char buf[MAX_PATH];
|
|
|
|
res = write_file_contents(outfn, str, TRUE);
|
|
DO_ASSERT(res == ERROR_SUCCESS);
|
|
|
|
res = read_file_contents(outfn, buf, MAX_PATH, NULL);
|
|
DO_ASSERT(res == ERROR_SUCCESS);
|
|
|
|
DO_ASSERT(0 == strcmp(buf, str));
|
|
|
|
/* this passes: but check out the file in emacs!
|
|
* apparently the bug works both ways. */
|
|
}
|
|
|
|
printf("All Test Passed\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|