summaryrefslogtreecommitdiff
path: root/ccan/ptr_valid/ptr_valid.h
blob: 3871cad8e7e462813ec7743b9b3234892b41e177 (plain)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Licensed under BSD-MIT: See LICENSE.
#ifndef CCAN_PTR_VALID_H
#define CCAN_PTR_VALID_H
#include "config.h"
#include <stdbool.h>
#include <stdlib.h>

/**
 * ptr_valid_read - can I safely read from a pointer?
 * @p: the proposed pointer.
 *
 * This function verifies that the pointer @p is safe to dereference for
 * reading.  It is very slow, particularly if the answer is "no".
 *
 * Sets errno to EFAULT on failure.
 *
 * See Also:
 *	ptr_valid_batch_read()
 */
#define ptr_valid_read(p)						\
	ptr_valid_r((p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))

/**
 * ptr_valid_write - can I safely write to a pointer?
 * @p: the proposed pointer.
 *
 * This function verifies that the pointer @p is safe to dereference
 * for writing (and reading).  It is very slow, particularly if the
 * answer is "no".
 *
 * Sets errno to EFAULT on failure.
 *
 * See Also:
 *	ptr_valid_batch_write()
 */
#define ptr_valid_write(p)						\
	ptr_valid_w((p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))

/**
 * ptr_valid_string - can I safely read a string?
 * @p: the proposed string.
 *
 * This function verifies that the pointer @p is safe to dereference
 * up to a nul character.  It is very slow, particularly if the answer
 * is "no".
 *
 * Sets errno to EFAULT on failure.
 *
 * See Also:
 *	ptr_valid_batch_string()
 */
bool ptr_valid_string(const char *p);

/**
 * ptr_valid - generic pointer check function
 * @p: the proposed pointer.
 * @align: the alignment requirements of the pointer.
 * @size: the size of the region @p should point to
 * @write: true if @p should be writable as well as readable.
 *
 * This function verifies that the pointer @p is safe to dereference.
 * It is very slow, particularly if the answer is "no".
 *
 * Sets errno to EFAULT on failure.
 *
 * See Also:
 *	ptr_valid_batch()
 */
bool ptr_valid(const void *p, size_t align, size_t size, bool write);

/**
 * struct ptr_valid_batch - pointer to store state for batch ptr ops
 *
 * Treat as private.
 */
struct ptr_valid_batch {
	unsigned int num_maps;
	struct ptr_valid_map *maps;
	int child_pid;
	int to_child, from_child;
	void *last;
	bool last_ok;
};

/**
 * ptr_valid_batch_start - prepare for a batch of ptr_valid checks.
 * @batch: an uninitialized ptr_valid_batch structure.
 *
 * This initializes @batch; this same @batch pointer can be reused
 * until the memory map changes (eg. via mmap(), munmap() or even
 * malloc() and free()).
 *
 * This is useful to check many pointers, because otherwise it can be
 * extremely slow.
 *
 * Example:
 * struct linked {
 *	struct linked *next;
 *	const char *str;
 * };
 *
 * static bool check_linked_carefully(struct linked *head)
 * {
 *	struct ptr_valid_batch batch;
 *	struct linked *old = head;
 *	bool half = true;
 *
 *	// If this fails, we can't check.  Assume OK.
 *	if (!ptr_valid_batch_start(&batch))
 *		return true;
 *
 *	while (head) {
 *		if (!ptr_valid_batch_read(&batch, head))
 *			goto fail;
 *		if (!ptr_valid_batch_string(&batch, head->str))
 *			goto fail;
 *		// Loop detection; move old at half speed of head.
 *		if (half)
 *			old = old->next;
 *		half = !half;
 *		if (head == old) {
 *			errno = ELOOP;
 *			goto fail;
 *		}
 *	}
 *	ptr_valid_batch_end(&batch);
 *	return true;
 *
 * fail:
 *	ptr_valid_batch_end(&batch);
 *	return false;
 * }
 *
 * See Also:
 *	ptr_valid_batch_stop()
 */
bool ptr_valid_batch_start(struct ptr_valid_batch *batch);

/**
 * ptr_valid_batch_read - can I safely read from a pointer?
 * @batch: the batch initialized by ptr_valid_batch_start().
 * @p: the proposed pointer.
 *
 * Batched version of ptr_valid_read().
 */
#define ptr_valid_batch_read(batch, p)					\
	ptr_valid_batch_r((batch),					\
			  (p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))

/**
 * ptr_valid_batch_write - can I safely write to a pointer?
 * @batch: the batch initialized by ptr_valid_batch_start().
 * @p: the proposed pointer.
 *
 * Batched version of ptr_valid_write().
 */
#define ptr_valid_batch_write(batch, p)					\
	ptr_valid_batch_w((batch),					\
			  (p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))

/**
 * ptr_valid_batch_string - can I safely read a string?
 * @batch: the batch initialized by ptr_valid_batch_start().
 * @p: the proposed string.
 *
 * Batched version of ptr_valid_string().
 */
bool ptr_valid_batch_string(struct ptr_valid_batch *batch, const char *p);

/**
 * ptr_valid_batch - generic batched pointer check function
 * @batch: the batch initialized by ptr_valid_batch_start().
 * @p: the proposed pointer.
 * @align: the alignment requirements of the pointer.
 * @size: the size of the region @p should point to
 * @write: true if @p should be writable as well as readable.
 *
 * Batched version of ptr_valid().
 */
bool ptr_valid_batch(struct ptr_valid_batch *batch,
		     const void *p, size_t alignment, size_t size, bool write);

/**
 * ptr_valid_batch_end - end a batch of ptr_valid checks.
 * @batch: a ptr_valid_batch structure.
 *
 * This is used after all checks are complete.
 *
 * See Also:
 *	ptr_valid_batch_start()
 */
void ptr_valid_batch_end(struct ptr_valid_batch *batch);


/* These wrappers get constness correct. */
static inline bool ptr_valid_r(const void *p, size_t align, size_t size)
{
	return ptr_valid(p, align, size, false);
}

static inline bool ptr_valid_w(void *p, size_t align, size_t size)
{
	return ptr_valid(p, align, size, true);
}

static inline bool ptr_valid_batch_r(struct ptr_valid_batch *batch,
				     const void *p, size_t align, size_t size)
{
	return ptr_valid_batch(batch, p, align, size, false);
}

static inline bool ptr_valid_batch_w(struct ptr_valid_batch *batch,
				     void *p, size_t align, size_t size)
{
	return ptr_valid_batch(batch, p, align, size, true);
}

struct ptr_valid_map {
	const char *start, *end;
	bool is_write;
};

#if HAVE_ALIGNOF
#define PTR_VALID_ALIGNOF(var) __alignof__(var)
#else
/* Can't check this... */
#define PTR_VALID_ALIGNOF(var) 1
#endif
#endif /* CCAN_PTR_VALID_H */