Coverage Report

Created: 2026-05-23 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sql-fuzzer.c
Line
Count
Source
1
/*
2
 * Copyright 2026 Google LLC
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
/*
18
 * SQL parser fuzzer for TDengine 3.x.
19
 *
20
 * This fuzzer exercises the SQL syntax parsing layer (qParseSqlSyntax),
21
 * which tokenises and builds an AST from untrusted SQL input without
22
 * requiring a running TDengine server or catalog.  It is the first
23
 * layer of SQL processing and is therefore the highest-risk entry
24
 * point for parsing bugs (buffer overflows, OOB reads, infinite loops,
25
 * assertion failures, etc.).
26
 */
27
28
#include <stdint.h>
29
#include <string.h>
30
31
/* Include the OS abstraction first so TDengine memory wrappers are defined
32
 * before any other TDengine headers redefine malloc/free. */
33
#include "os.h"
34
#include "parser.h"
35
#include "catalog.h"
36
#include "querynodes.h"
37
38
/* One-time initialisation flag. */
39
static int g_init = 0;
40
41
/* Error message buffer reused every call. */
42
4.98k
#define MSG_BUF_LEN 4096
43
static char g_msgBuf[MSG_BUF_LEN];
44
45
4.98k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
46
  /* Initialise keyword table once. */
47
4.98k
  if (!g_init) {
48
1
    qInitKeywordsTable();
49
1
    g_init = 1;
50
1
  }
51
52
  /* Require at least one byte so we always have a null-terminated string. */
53
4.98k
  if (size == 0) {
54
0
    return 0;
55
0
  }
56
57
  /* Copy input and null-terminate so that the parser can treat it as a
58
   * C-string safely.  We deliberately limit to 64 KB to keep the fuzzer
59
   * fast; real SQL queries are never this long in practice. */
60
4.98k
  if (size > 65536) {
61
16
    size = 65536;
62
16
  }
63
  /* Use TDengine's memory allocator (direct malloc/free are forbidden). */
64
4.98k
  char *sql = (char *)taosMemoryMalloc(size + 1);
65
4.98k
  if (!sql) {
66
0
    return 0;
67
0
  }
68
4.98k
  memcpy(sql, data, size);
69
4.98k
  sql[size] = '\0';
70
71
  /* Prepare a minimal parse context.
72
   * allocatorId = 0 → use the default (no custom arena allocator).
73
   * parseOnly is intentionally left false; we set up enough context
74
   * for syntax-only parsing via qParseSqlSyntax. */
75
4.98k
  SParseContext cxt;
76
4.98k
  memset(&cxt, 0, sizeof(cxt));
77
4.98k
  cxt.acctId        = 1;
78
4.98k
  cxt.db            = "fuzz_db";
79
4.98k
  cxt.pUser         = "root";
80
4.98k
  cxt.isSuperUser   = true;
81
4.98k
  cxt.enableSysInfo = true;
82
4.98k
  cxt.privInfo      = (uint16_t)0xFFFF;  /* grant all privileges */
83
4.98k
  cxt.pSql          = sql;
84
4.98k
  cxt.sqlLen        = size;
85
4.98k
  cxt.pMsg          = g_msgBuf;
86
4.98k
  cxt.msgLen        = MSG_BUF_LEN;
87
4.98k
  cxt.svrVer        = "3.0.0.0";
88
4.98k
  cxt.allocatorId   = 0;
89
90
4.98k
  SQuery      *pQuery     = NULL;
91
4.98k
  SCatalogReq  catalogReq;
92
4.98k
  memset(&catalogReq, 0, sizeof(catalogReq));
93
94
  /* Parse SQL syntax only – no network/catalog required. */
95
4.98k
  (void)qParseSqlSyntax(&cxt, &pQuery, &catalogReq);
96
97
  /* Clean up. */
98
4.98k
  if (pQuery) {
99
2.03k
    qDestroyQuery(pQuery);
100
2.03k
  }
101
102
4.98k
  taosMemoryFree(sql);
103
4.98k
  return 0;
104
4.98k
}