PostgreSQL Source Code: src/test/modules/test_escape/test_escape.c Source File (original) (raw)

1

2

3

4

5

6

7

8

9

11

13#include <stdio.h>

14

22

23

25{

30

34

35#define NEVER_ACCESS_STR "\xff never-to-be-touched"

36

37

38

39

40

42{

44

45

46

47

48

50

51

52

53

54

56

57

58

59

60

62

63

64

65

67

69 const char *unescaped, size_t unescaped_len,

72

73

74

75

77{

82

83

84

85

86

88 NULL

89};

90

91

92

93

94

95

96

97

98

99static void

101{

102 for (size_t i = 0; i < len; i++)

103 {

104 char c = *str;

105

106 if (c == '\n')

108 else if (c == '\0')

112 else

115 }

116}

117

118static void

121 const char *testname,

122 const char *details,

124 const char *resultdesc)

125{

127 bool print_details = true;

128 bool print_result = true;

129

131 {

133 print_details = false;

135 print_result = false;

136 }

137 else

139

140 if (print_details)

141 printf("%s", details);

142

143 if (print_result)

144 printf("%s %d - %s: %s: %s\n",

145 success ? "ok" : "not ok",

146 test_id, testname,

148 resultdesc);

149}

150

151

152

153

154

155static bool

157{

158

159

160

161

163 return true;

164 return false;

165}

166

167

168

169

170

171

172

173

174

175

176

177

178

179static void

181{

183 size_t input_len = 0x20000;

185

186

188 memset(input, '-', input_len - 1);

189 input[input_len - 1] = 0xfe;

190

191

196

197

200 testname->data, "",

201 "input validity vs escape success", "ok");

202

205}

206

207

208

209

210

211

212static void

214{

217 const char input[] = "{\"\\u\xFE";

218 size_t input_len = sizeof(input) - 1;

222

223

228 raw_buf->len - input_len);

229

230

235

236

241 testname->data, "",

243

247}

248

249

250static bool

252 const char *unescaped, size_t unescaped_len,

254{

255 char *escaped;

256

258 if (!escaped)

259 {

261 escape_err->data[escape_err->len - 1] = 0;

262 escape_err->len--;

263 return false;

264 }

265 else

266 {

269 return true;

270 }

271}

272

273static bool

275 const char *unescaped, size_t unescaped_len,

277{

278 char *escaped;

279

281 if (!escaped)

282 {

284 escape_err->data[escape_err->len - 1] = 0;

285 escape_err->len--;

286 return false;

287 }

288 else

289 {

292 return true;

293 }

294}

295

296static bool

298 const char *unescaped, size_t unescaped_len,

300{

302 size_t sz;

303

307 unescaped, unescaped_len,

309

310 target->len += sz;

312

314 {

316 escape_err->data[escape_err->len - 1] = 0;

317 escape_err->len--;

318 return false;

319 }

320 else

321 {

322 return true;

323 }

324}

325

326static bool

328 const char *unescaped, size_t unescaped_len,

330{

331 size_t sz;

332

336 unescaped, unescaped_len);

337 target->len += sz;

339

340

341 return true;

342}

343

344

345

346

347

348

349static bool

351 const char *unescaped, size_t unescaped_len,

353{

354 const char *s = unescaped;

355

357

358 for (int i = 0; i < unescaped_len; i++)

359 {

360 char c = *s;

361

362 if (c == '\'')

363 {

365 }

366 else

368 s++;

369 }

371

372 return true;

373}

374

375static bool

377 const char *unescaped, size_t unescaped_len,

379{

381

382 return true;

383}

384

385static bool

387 const char *unescaped, size_t unescaped_len,

389{

392

393 return true;

394}

395

397{

398 {

399 .name = "PQescapeLiteral",

400 .reports_errors = true,

401 .supports_input_length = true,

403 },

404 {

405 .name = "PQescapeIdentifier",

406 .reports_errors = true,

407 .supports_input_length = true,

409 },

410 {

411 .name = "PQescapeStringConn",

412 .reports_errors = true,

413 .supports_input_length = true,

415 },

416 {

417 .name = "PQescapeString",

418 .reports_errors = false,

419 .supports_input_length = true,

421 },

422 {

423 .name = "replace",

424 .reports_errors = false,

425 .supports_only_valid = true,

426 .supports_only_ascii_overlap = true,

427 .supports_input_length = true,

429 },

430 {

431 .name = "appendStringLiteral",

432 .reports_errors = false,

434 },

435 {

436 .name = "fmtId",

437 .reports_errors = false,

439 },

440};

441

442

443#define TV(enc, string) {.client_encoding = (enc), .escape=string, .escape_len=sizeof(string) - 1, }

444#define TV_LEN(enc, string, len) {.client_encoding = (enc), .escape=string, .escape_len=len, }

446{

447

448 TV("UTF-8", "1"),

449 TV("UTF-8", "'"),

450 TV("UTF-8", "\""),

451

452 TV("UTF-8", "\'"),

453 TV("UTF-8", "\""),

454

455 TV("UTF-8", "\\"),

456

457 TV("UTF-8", "\\'"),

458 TV("UTF-8", "\\\""),

459

460

461 TV("UTF-8", "1\xC0"),

462 TV("UTF-8", "1\xE0 "),

463 TV("UTF-8", "1\xF0 "),

464 TV("UTF-8", "1\xF0 "),

465 TV("UTF-8", "1\xF0 "),

466

467

468 TV("UTF-8", "1\xE0"),

469 TV("UTF-8", "1\xF0"),

470 TV("UTF-8", "\xF0"),

471

472

473 TV("UTF-8", "1\xE0'"),

474 TV("UTF-8", "1\xE0\""),

475 TV("UTF-8", "1\xF0'"),

476 TV("UTF-8", "1\xF0\""),

477 TV("UTF-8", "1\xF0'; "),

478 TV("UTF-8", "1\xF0\"; "),

479 TV("UTF-8", "1\xF0';;;;"),

480 TV("UTF-8", "1\xF0 ';;;;"),

481 TV("UTF-8", "1\xF0 \";;;;"),

482 TV("UTF-8", "1\xE0'; \\l ; "),

483 TV("UTF-8", "1\xE0\"; \\l ; "),

484

485

486 TV("UTF-8", "some\0thing"),

487 TV("UTF-8", "some\0"),

488 TV("UTF-8", "some\xF0'\0"),

489 TV("UTF-8", "some\xF0'\0'"),

490 TV("UTF-8", "some\xF0" "ab\0'"),

491

492

493 TV("GB18030", "\x90\x31"),

494 TV("GB18030", "\\\x81\x5c'"),

495 TV("GB18030", "\\\x81\x5c\""),

496 TV("GB18030", "\\\x81\x5c\0'"),

497

498

499

500

501

502

503 TV("GB18030", "\\\x81';"),

504 TV("GB18030", "\\\x81\";"),

505

506

507

508

509 TV("GB18030", "\\\x81\\';"),

510 TV("GB18030", "\\\x81\\\";"),

511 TV("GB18030", "\\\x81\0;"),

512 TV("GB18030", "\\\x81\0'"),

513 TV("GB18030", "\\\x81'\0"),

514

515 TV("SJIS", "\xF0\x40;"),

516

517 TV("SJIS", "\xF0';"),

518 TV("SJIS", "\xF0\";"),

519 TV("SJIS", "\xF0\0'"),

520 TV("SJIS", "\\\xF0\\';"),

521 TV("SJIS", "\\\xF0\\\";"),

522

523 TV("gbk", "\x80';"),

524 TV("gbk", "\x80"),

525 TV("gbk", "\x80'"),

526 TV("gbk", "\x80\""),

527 TV("gbk", "\x80\\"),

528

529 TV("mule_internal", "\\\x9c';\0;"),

530

531 TV("sql_ascii", "1\xC0'"),

532

533

534

535

536

537

538

539

540

541

542 TV_LEN("gbk", "\x80", 1),

543 TV_LEN("GB18030", "\x80", 1),

544 TV_LEN("GB18030", "\x80\0", 2),

545 TV_LEN("GB18030", "\x80\x30", 2),

546 TV_LEN("GB18030", "\x80\x30\0", 3),

547 TV_LEN("GB18030", "\x80\x30\x30", 3),

548 TV_LEN("GB18030", "\x80\x30\x30\0", 4),

549 TV_LEN("UTF-8", "\xC3\xb6 ", 1),

550 TV_LEN("UTF-8", "\xC3\xb6 ", 2),

551};

552

553

554static const char *

556{

557#define TOSTR_CASE(sym) case sym: return #sym

558

559 switch (res)

560 {

565 }

566

568 return "";

569}

570

571

572

573

574

575

576static void

579{

584 int matches = 0;

585 bool test_fails;

586 const char *resdesc;

587

589

591

592

593

594

595

598

599 do

600 {

602

603 scan_result = psql_scan(scan_state, query_buf,

604 &prompt_status);

605

607 "#\t\t %d: scan_result: %s prompt: %u, query_buf: ",

608 matches, scan_res_s(scan_result), prompt_status);

611

612 matches++;

613 }

615

618

619 test_fails = matches > 1 || scan_result != PSCAN_EOL;

620

621 if (matches > 1)

622 resdesc = "more than one match";

623 else if (scan_result != PSCAN_EOL)

624 resdesc = "unexpected end state";

625 else

626 resdesc = "ok";

627

629 "psql parse",

630 resdesc);

631}

632

633static void

635{

641 size_t input_encoding_validlen;

642 bool input_encoding_valid;

643 size_t input_encoding0_validlen;

644 bool input_encoding0_valid;

645 bool escape_success;

646 size_t escape_encoding_length;

647 bool escape_encoding_valid;

648

654

657 {

658 goto out;

659 }

660

661

666

667

674

675

676

680 input_encoding_valid = input_encoding_validlen == tv->escape_len;

682 input_encoding_valid);

683

688 appendPQExpBuffer(details, "#\t input encoding valid till 0: %d\n",

689 input_encoding0_valid);

690

693

696 goto out;

697

698

699

700

701

702

703

704

705

706

707

708

709

710

712

714 {

715

716

717

718

720

723 }

724 else

725 {

726

729

732 }

733

734

735 escape_success = ef->escape(tc->conn, escape_buf,

737 escape_err);

738 if (!escape_success)

739 {

741 escape_err->data);

742 }

743

744 if (escape_buf->len > 0)

745 {

746 bool contains_never;

747

748 appendPQExpBuffer(details, "#\t escaped string: %zd bytes: ", escape_buf->len);

751

753 escape_buf->data,

754 escape_buf->len);

755 escape_encoding_valid = escape_encoding_length == escape_buf->len;

756

758 escape_encoding_valid);

759

760

761

762

763

764

767 "escaped data beyond end of input",

768 contains_never ? "no" : "all secrets revealed");

769 }

770 else

771 {

772 escape_encoding_length = 0;

773 escape_encoding_valid = 1;

774 }

775

776

777

778

779

780

781

782

784 {

785 bool ok = true;

786 const char *resdesc = "ok";

787

788 if (escape_success)

789 {

790 if (!input_encoding0_valid)

791 {

792 ok = false;

793 resdesc = "invalid input escaped successfully";

794 }

795 else if (!input_encoding_valid)

796 resdesc = "invalid input escaped successfully, due to zero byte";

797 }

798 else

799 {

800 if (input_encoding0_valid)

801 {

802 ok = false;

803 resdesc = "valid input failed to escape";

804 }

805 else if (input_encoding_valid)

806 resdesc = "valid input failed to escape, due to zero byte";

807 }

808

810 "input validity vs escape success",

811 resdesc);

812 }

813

814

815

816

817

818 {

819 bool ok = true;

820 const char *resdesc = "ok";

821

822 if (input_encoding0_valid && !input_encoding_valid && escape_encoding_valid)

823 {

824 resdesc = "invalid input produced valid output, due to zero byte";

825 }

826 else if (input_encoding0_valid && !escape_encoding_valid)

827 {

828 ok = false;

829 resdesc = "valid input produced invalid output";

830 }

831 else if (!input_encoding0_valid &&

833 escape_encoding_valid)

834 {

835 ok = false;

836 resdesc = "invalid input produced valid output";

837 }

838

840 "input and escaped encoding validity",

841 resdesc);

842 }

843

844

845

846

847

848 if (escape_buf->len > 0)

849 {

851 escape_buf, details);

852 }

853

854out:

860}

861

862static void

864{

866 {

867 fprintf(stderr, "failed to set encoding to %s:\n%s\n",

869 exit(1);

870 }

871

873 {

875

877 }

878}

879

880static void

882{

883 if (hint)

884 fprintf(stderr, "Error: %s\n\n", hint);

885

886 printf("PostgreSQL escape function test\n"

887 "\n"

888 "Usage:\n"

889 " test_escape --conninfo=CONNINFO [OPTIONS]\n"

890 "\n"

891 "Options:\n"

892 " -h, --help show this help\n"

893 " -c, --conninfo=CONNINFO connection information to use\n"

894 " -v, --verbose show test details even for successes\n"

895 " -q, --quiet only show failures\n"

896 " -f, --force-unsupported test invalid input even if unsupported\n"

897 );

898

899 if (hint)

900 exit(1);

901}

902

903int

904main(int argc, char *argv[])

905{

907 int c;

908 int option_index;

909

910 static const struct option long_options[] = {

915 {"force-unsupported", no_argument, NULL, 'f'},

916 {NULL, 0, NULL, 0},

917 };

918

919 while ((c = getopt_long(argc, argv, "c:fhqv", long_options, &option_index)) != -1)

920 {

921 switch (c)

922 {

923 case 'h':

925 exit(0);

926 break;

927 case 'c':

929 break;

930 case 'v':

932 break;

933 case 'q':

935 break;

936 case 'f':

938 break;

939 }

940 }

941

942 if (argc - optind >= 1)

943 usage("unused option(s) specified");

944

946 usage("--conninfo needs to be specified");

947

949

951 {

952 fprintf(stderr, "could not connect: %s\n",

954 exit(1);

955 }

956

959

961 {

963 }

964

966

970}

#define fprintf(file, fmt, msg)

PGconn * PQconnectdb(const char *conninfo)

ConnStatusType PQstatus(const PGconn *conn)

int PQclientEncoding(const PGconn *conn)

void PQfinish(PGconn *conn)

char * PQerrorMessage(const PGconn *conn)

int PQsetClientEncoding(PGconn *conn, const char *encoding)

void PQfreemem(void *ptr)

size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)

size_t PQescapeString(char *to, const char *from, size_t length)

char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)

char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)

void * pg_malloc(size_t size)

int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)

#define required_argument

JsonParseErrorType pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)

JsonLexContext * makeJsonLexContextCstringLen(JsonLexContext *lex, const char *json, size_t len, int encoding, bool need_escapes)

char * json_errdetail(JsonParseErrorType error, JsonLexContext *lex)

void freeJsonLexContext(JsonLexContext *lex)

@ JSON_UNICODE_ESCAPE_FORMAT

#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)

PGDLLIMPORT char * optarg

#define PG_ENCODING_BE_LAST

size_t strnlen(const char *str, size_t maxlen)

PQExpBuffer createPQExpBuffer(void)

int enlargePQExpBuffer(PQExpBuffer str, size_t needed)

void resetPQExpBuffer(PQExpBuffer str)

void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)

void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)

void destroyPQExpBuffer(PQExpBuffer str)

void appendPQExpBufferChar(PQExpBuffer str, char ch)

void appendPQExpBufferStr(PQExpBuffer str, const char *data)

enum _promptStatus promptStatus_t

void psql_scan_destroy(PsqlScanState state)

PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt)

PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks)

void psql_scan_setup(PsqlScanState state, const char *line, int line_len, int encoding, bool std_strings)

const char * fmtId(const char *rawid)

void setFmtEncoding(int encoding)

void appendStringLiteral(PQExpBuffer buf, const char *str, int encoding, bool std_strings)

bool supports_only_ascii_overlap

bool supports_input_length

bool(* escape)(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

const char * client_encoding

static void test_gb18030_json(pe_test_config *tc)

int main(int argc, char *argv[])

static bool escape_replace(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

static void escapify(PQExpBuffer buf, const char *str, size_t len)

static bool escape_string(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

static bool escape_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

#define TV_LEN(enc, string, len)

static bool escape_append_literal(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

struct pe_test_config pe_test_config

static void test_one_vector(pe_test_config *tc, const pe_test_vector *tv)

static pe_test_escape_func pe_test_escape_funcs[]

static const char * scan_res_s(PsqlScanResult res)

static pe_test_vector pe_test_vectors[]

static void report_result(pe_test_config *tc, bool success, const char *testname, const char *details, const char *subname, const char *resultdesc)

static void test_psql_parse(pe_test_config *tc, PQExpBuffer testname, PQExpBuffer input_buf, PQExpBuffer details)

struct pe_test_vector pe_test_vector

static bool escape_string_conn(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

static bool escape_identifier(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

static void usage(const char *hint)

static void test_one_vector_escape(pe_test_config *tc, const pe_test_vector *tv, const pe_test_escape_func *ef)

struct pe_test_escape_func pe_test_escape_func

static bool encoding_conflicts_ascii(int encoding)

static const PsqlScanCallbacks test_scan_callbacks

static bool escape_fmt_id(PGconn *conn, PQExpBuffer target, const char *unescaped, size_t unescaped_len, PQExpBuffer escape_err)

static void test_gb18030_page_multiple(pe_test_config *tc)

int pg_encoding_verifymbstr(int encoding, const char *mbstr, int len)