// Licensed under BSD-MIT: See LICENSE.
#ifndef CCAN_PTR_VALID_H
#define CCAN_PTR_VALID_H
#include <hkl/ccan/ccan_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 */
