18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This code tests that the current task stack is properly erased (filled
48c2ecf20Sopenharmony_ci * with STACKLEAK_POISON).
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Authors:
78c2ecf20Sopenharmony_ci *   Alexander Popov <alex.popov@linux.com>
88c2ecf20Sopenharmony_ci *   Tycho Andersen <tycho@tycho.ws>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "lkdtm.h"
128c2ecf20Sopenharmony_ci#include <linux/stackleak.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_civoid lkdtm_STACKLEAK_ERASING(void)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	unsigned long *sp, left, found, i;
178c2ecf20Sopenharmony_ci	const unsigned long check_depth =
188c2ecf20Sopenharmony_ci			STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
198c2ecf20Sopenharmony_ci	bool test_failed = false;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	/*
228c2ecf20Sopenharmony_ci	 * For the details about the alignment of the poison values, see
238c2ecf20Sopenharmony_ci	 * the comment in stackleak_track_stack().
248c2ecf20Sopenharmony_ci	 */
258c2ecf20Sopenharmony_ci	sp = PTR_ALIGN(&i, sizeof(unsigned long));
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
288c2ecf20Sopenharmony_ci	sp--;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * One 'long int' at the bottom of the thread stack is reserved
328c2ecf20Sopenharmony_ci	 * and not poisoned.
338c2ecf20Sopenharmony_ci	 */
348c2ecf20Sopenharmony_ci	if (left > 1) {
358c2ecf20Sopenharmony_ci		left--;
368c2ecf20Sopenharmony_ci	} else {
378c2ecf20Sopenharmony_ci		pr_err("FAIL: not enough stack space for the test\n");
388c2ecf20Sopenharmony_ci		test_failed = true;
398c2ecf20Sopenharmony_ci		goto end;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	pr_info("checking unused part of the thread stack (%lu bytes)...\n",
438c2ecf20Sopenharmony_ci					left * sizeof(unsigned long));
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * Search for 'check_depth' poison values in a row (just like
478c2ecf20Sopenharmony_ci	 * stackleak_erase() does).
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	for (i = 0, found = 0; i < left && found <= check_depth; i++) {
508c2ecf20Sopenharmony_ci		if (*(sp - i) == STACKLEAK_POISON)
518c2ecf20Sopenharmony_ci			found++;
528c2ecf20Sopenharmony_ci		else
538c2ecf20Sopenharmony_ci			found = 0;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (found <= check_depth) {
578c2ecf20Sopenharmony_ci		pr_err("FAIL: the erased part is not found (checked %lu bytes)\n",
588c2ecf20Sopenharmony_ci						i * sizeof(unsigned long));
598c2ecf20Sopenharmony_ci		test_failed = true;
608c2ecf20Sopenharmony_ci		goto end;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	pr_info("the erased part begins after %lu not poisoned bytes\n",
648c2ecf20Sopenharmony_ci				(i - found) * sizeof(unsigned long));
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* The rest of thread stack should be erased */
678c2ecf20Sopenharmony_ci	for (; i < left; i++) {
688c2ecf20Sopenharmony_ci		if (*(sp - i) != STACKLEAK_POISON) {
698c2ecf20Sopenharmony_ci			pr_err("FAIL: bad value number %lu in the erased part: 0x%lx\n",
708c2ecf20Sopenharmony_ci								i, *(sp - i));
718c2ecf20Sopenharmony_ci			test_failed = true;
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciend:
768c2ecf20Sopenharmony_ci	if (test_failed) {
778c2ecf20Sopenharmony_ci		pr_err("FAIL: the thread stack is NOT properly erased\n");
788c2ecf20Sopenharmony_ci		dump_stack();
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		pr_info("OK: the rest of the thread stack is properly erased\n");
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci}
83