1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
/*
* Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/**
* @file asn1_string_poison_test.c
* Checks that the NUL terminator libcrypto writes after ASN1_STRING data is
* inaccessible under AddressSanitizer, MemorySanitizer and Valgrind. Run
* with no argument the program reads the terminator with strlen() and is
* expected to be killed by the sanitizer; run with "counted" it reads only
* the counted bytes and is expected to exit successfully. Without a
* sanitizer the strlen() run exits with failure itself; with one, surviving
* the strlen() exits successfully, which the recipe reports as a failure.
* Under Valgrind the process survives the strlen() and memcheck reports the
* read. When OSSL_VALGRIND_CT is set the test framework runs memcheck with
* --error-exitcode=1, and the program exits successfully after the strlen()
* the same way, leaving the exit status to memcheck; under any other
* Valgrind wrapper it exits with failure.
*/
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include "testutil.h"
#if defined(__has_feature)
#if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer)
#define HAVE_SANITIZER 1
#endif
#endif /* defined(__has_feature) */
#if defined(__SANITIZE_ADDRESS__) && !defined(HAVE_SANITIZER)
#define HAVE_SANITIZER 1
#endif
#if defined __has_include
/* Any compiler you're going to run valgrind on has this */
#if __has_include(<valgrind/valgrind.h>)
#include <valgrind/valgrind.h>
#endif
#endif /* defined(__has_include) */
/* DER UTF8String "hello" */
static const unsigned char der[] = { 0x0c, 0x05, 'h', 'e', 'l', 'l', 'o' };
/**
* @brief Report whether a checker decides this run's exit status.
* That is a sanitizer, or memcheck run by the test framework with
* --error-exitcode=1 (OSSL_VALGRIND_CT).
* @returns 1 when the checker sets the exit status, 0 otherwise
*/
static int checker_sets_exit_status(void)
{
#if defined(HAVE_SANITIZER)
return 1;
#else
#if defined(RUNNING_ON_VALGRIND)
if (RUNNING_ON_VALGRIND && getenv("OSSL_VALGRIND_CT") != NULL)
return 1;
#endif
return 0;
#endif /* defined(HAVE_SANITIZER) */
}
/*
* A plain main() rather than the test framework's: the failing run is
* expected to die inside the sanitizer, which the framework would report
* as a crash.
*/
int main(int argc, char *argv[])
{
const unsigned char *p = der;
ASN1_UTF8STRING *str = d2i_ASN1_UTF8STRING(NULL, &p, sizeof(der));
const unsigned char *data;
volatile size_t sink;
int exitcode = EXIT_FAILURE;
if (!TEST_ptr(str)
|| !TEST_size_t_eq(ASN1_STRING_get_length(str), 5))
goto end;
data = ASN1_STRING_get0_data(str);
if (argc > 1 && strcmp(argv[1], "counted") == 0) {
/* Counted access never touches the terminator. */
if (TEST_mem_eq(data, ASN1_STRING_get_length(str), "hello", 5))
exitcode = EXIT_SUCCESS;
goto end;
}
/*
* Reads the terminator; a sanitizer kills the process here, memcheck
* reports the read and lets the process continue.
*/
sink = strlen((const char *)data);
(void)sink;
if (checker_sets_exit_status()) {
/*
* Reached under a sanitizer only when it did not report the read.
* The recipe expects this run to fail: under a sanitizer a
* successful exit is that failure; under memcheck the exit status
* is replaced by --error-exitcode when the read was reported.
*/
TEST_note("strlen() on ASN1_STRING data survived");
exitcode = EXIT_SUCCESS;
}
end:
ASN1_UTF8STRING_free(str);
return exitcode;
}
|