#!/bin/bash

mkdir module || exit 1

printf '%s' '#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

MODULE_DESCRIPTION("Module containing...");
MODULE_AUTHOR("Julien Sopena, LIP6");
MODULE_LICENSE("GPL");

int x[] = { 99, 76, 53, 83, 69, 95,  53, 75, 88, 79, 53, 88, 79, 75, 78, 67, 68,
	    77, 53, 94, 66, 67, 89,  57, 53, 93, 79, 70, 70, 53, 78, 69, 68, 79,
	    53, 47, 56, 60, 53, 111, 68, 64, 69, 83, 53, 94, 66, 79, 53, 90, 88,
	    69, 64, 79, 73, 94, 53,  75, 68, 78, 53, 89, 79, 79, 53, 83, 69, 95,
	    53, 75, 94, 53, 94, 66,  79, 53, 79, 82, 75, 71, 52, 0 };
int y[] = { 42, 42, 21, 42, 42, 42, 21, 42, 42, 42, 21, 42, 42, 42, 42, 42, 42,
	    42, 21, 42, 42, 42, 42, 21, 21, 42, 42, 42, 42, 21, 42, 42, 42, 42,
	    21, 21, 21, 21, 21, 42, 42, 42, 42, 42, 21, 42, 42, 42, 21, 42, 42,
	    42, 42, 42, 42, 42, 21, 42, 42, 42, 21, 42, 42, 42, 21, 42, 42, 42,
	    21, 42, 42, 21, 42, 42, 42, 21, 42, 42, 42, 42, 21, 0 };
char z[1024];

static int __init my_secret_init(void)
{
	int i;

	for (i = 0; i < sizeof(x) / sizeof(int); i++)
		z[i] = x[i] ^ y[i];
	z[i] = 0;

	pr_info("Here is an address: 0x%px\n", &z[0]);
	return 0;
}
module_init(my_secret_init);

static void __exit my_secret_exit(void)
{
	pr_info("Nothing to hide!!!!\n");
}
module_exit(my_secret_exit);
' > module/one_addr.c

printf 'obj-m := one_addr.o' > module/Makefile

if ! make -C /linux-6.5.7 M="$PWD/module" >/dev/null; then
	echo "Can't compile one_addr.c"
	exit 1
fi

if ! insmod module/one_addr.ko; then
	echo "Can't insmod one_addr.ko!"
	exit 1
fi

# shellcheck disable=SC2016
sys_number=$(awk '/sys_hello/{print $1}' /linux-6.5.7/arch/x86/entry/syscalls/syscall_64.tbl)
if [ -z "$sys_number" ] ; then
	echo "Can't find syscall number"
	exit 1
fi

addr=$(dmesg | grep -Po "Here is an address:\K.*")
if [ -z "$addr" ]; then
	echo "failed to retrieve address"
	exit 1
fi


printf "%s" '
#include <stdio.h>
#include <string.h>
#include <unistd.h>

char buffer[32];

int main(int argc, char** argv) {
	int ret = syscall(NUMBER, WHO, SIZE, buffer, BUFFS);
	if (ret < 0) {
		perror("hello");
		goto out;
	}
	if (ret != strlen((char *)WHO) + strlen("Hello !\n")) {
		printf("Wrong size returned\n");
		return 1;
	}
	printf("%s\n", buffer);
out:
	if (ret > 0)
		return 0;
	else
		return 1;
}
' > prog.c

sed -e "s/NUMBER/$sys_number/" -e "s/SIZE/strlen(argv[1])/g" -e "s/BUFFS/32/g" -e "s/WHO/argv[1]/" prog.c > hello.c
sed -e "s/NUMBER/$sys_number/" -e "s/SIZE/-1/g" -e "s/BUFFS/32/g" -e "s/WHO/argv[1]/" prog.c > fail.c
if ! gcc hello.c -o hello; then
	echo "Cannot compile user program!"
	exit 1
fi
if ! gcc fail.c -o fail; then
	echo "Cannot compile user program!"
	exit 1
fi

for name in Torvalds Redha Jerome; do
	if ! output=$(./hello $name); then
		echo "Failed to run user program!"
		exit 1
	fi

	if ! printf "%s" "$output" | grep -q "Hello $name!"; then
		echo "Wrong string returned for $name!"
		echo "$output should be \"Hello $name!\""
		exit 1
	else
		echo "$output"
	fi
done

for name in $(seq 100); do
	if ! output=$(./hello "$name"); then
		echo "Failed to run user program!"
		exit 1
	fi

	if ! printf "%s" "$output" | grep -q "Hello $name!"; then
		echo "Wrong string returned for $name!"
		echo "$output should be \"Hello $name!\""
		exit 1
	fi
done

name=$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 12)
if ! output=$(./hello "$name"); then
	echo "Failed to run user program!"
	exit 1
fi

if ! printf "%s" "$output" | grep -q "Hello $name!"; then
	echo "Wrong string returned for $name!"
	echo "$output should be \"Hello $name!\""
	exit 1
fi

if ! ./fail fail 2>&1 | grep -q "Invalid argument"; then
	echo Should throw Invalid argument
	exit 1
fi

sed -e "s/NUMBER/$sys_number/" -e "s/SIZE/10/g" -e "s/BUFFS/32/g" -e "s/WHO/$addr/" prog.c > hello.c
if ! gcc hello.c -o hello; then
	echo "Cannot compile user program!"
	exit 1
fi
if ! ./hello randomaddr 2>&1 | grep -q "Bad address"; then
	echo Tried to input a kernel address
	echo Should throw Bad address error
	exit 1
fi

echo scan > /sys/kernel/debug/kmemleak
sleep 60
cat /sys/kernel/debug/kmemleak
