/* * Sysfs for OASES framework * * Copyright (C) 2016 Baidu, Inc. All Rights Reserved. * * You should have received a copy of license along with this program; * if not, ask for it from Baidu, Inc. * */ #include #include #include #include #include #include #include "util.h" #include "patch_info.h" #include "patch_base.h" #include "patch_api.h" #include "patch_mgr.h" #ifdef CONFIG_OASES_PERSIST_MEMORY #include "pmem.h" #endif /* * Oases Sysfs Interface * * /sys/kernel/oases * /sys/kernel/oases/boot * /sys/kernel/oases/state * /sys/kernel/oases/version * * /sys/kernel/oases/ * /sys/kernel/oases//enabled (0/1) * /sys/kernel/oases//log * /sys/kernel/oases//info * * patch: $VENDOR-$NAME-$ID */ #define PROC_OASES_STATE "state" #define SYSFS_OASES "oases" #define SYSFS_OASES_VERSION "version" #define SYSFS_PATCH_ENABLED "enabled" #define SYSFS_PATCH_LOG "log" #define ARCH_ARM "arm" #define ARCH_AARCH64 "aarch64" extern struct list_head patchinfo_list; extern struct mutex oases_mutex; static struct kobject *sysfs_oases = NULL; static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct oases_patch_info *info; info = container_of(kobj, struct oases_patch_info, kobj); return snprintf(buf, 16, "%u\n", info->status); } static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int ret; long enabled = 0; struct oases_patch_info *info; /* oases_debug("buf=%s, count=%ld\n", buf, count); buf=1\x0a, count=2 */ ret = kstrtol(buf, 10, &enabled); if (ret < 0) return -EINVAL; if (enabled != STATUS_ENABLED && enabled != STATUS_DISABLED) return -EINVAL; if (!mutex_trylock(&oases_mutex)) return -EBUSY; /* treat it like success if trying to update info->status with same value */ info = container_of(kobj, struct oases_patch_info, kobj); if (info->status == enabled) { ret = count; oases_debug("Warning: updating info->status(%d) with enabled(%ld)\n", info->status, enabled); goto end; } if (enabled == STATUS_DISABLED) ret = oases_insn_unpatch(info); else /* STATUS_ENABLED */ ret = oases_insn_patch(info); if (ret) { oases_error("%s %d -> %ld failed with %d\n", info->id, info->status, enabled, ret); goto end; } info->status = enabled; ret = count; end: mutex_unlock(&oases_mutex); return ret; } static ssize_t log_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct oases_patch_info *info; struct oases_attack_log *cur; int i; ssize_t ret = 0; unsigned long flags; info = container_of(kobj, struct oases_patch_info, kobj); spin_lock_irqsave(&info->log_lock, flags); for (i = 0; i < info->log_index; i++) { cur = info->plog + i; if (!cur->count) continue; ret += scnprintf(buf + ret, PAGE_SIZE - ret - 1, "%ld %ld %ld %ld\n", cur->uid, cur->count, cur->start_time, cur->end_time); } spin_unlock_irqrestore(&info->log_lock, flags); return ret; } static ssize_t info_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct oases_patch_info *ctx; struct oases_patch_entry *patch, *p; int count = 0; ctx = container_of(kobj, struct oases_patch_info, kobj); list_for_each_entry_safe(patch, p, &ctx->patches, list) { count += kp_vtab(patch)->show_info(patch, kobj, attr, buf, count); } return count; } static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return snprintf(buf, 16, "%u\n", OASES_VERSION); } static ssize_t arch_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { #ifdef CONFIG_ARM return snprintf(buf, 16, "%s\n", ARCH_ARM); #else /* aarch 64 */ return snprintf(buf, 16, "%s\n", ARCH_AARCH64); #endif } static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct oases_patch_info *info; ssize_t ret = 0; mutex_lock(&oases_mutex); list_for_each_entry(info, &patchinfo_list, list) { ret += scnprintf(buf + ret, PAGE_SIZE - ret - 1, "%s %s\n", info->id, info->status ? "enabled" : "disabled"); } mutex_unlock(&oases_mutex); return ret; } #ifdef CONFIG_OASES_PERSIST_MEMORY static ssize_t boot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int ret; ret = scnprintf(buf, PAGE_SIZE, "%d\n", oases_boot_reason); return ret; } #endif static struct kobj_attribute attribute_version = __ATTR(version, S_IRUGO, version_show, NULL); static struct kobj_attribute attribute_state = __ATTR(state, S_IRUGO, state_show, NULL); static struct kobj_attribute attribute_arch = __ATTR(arch, S_IRUGO, arch_show, NULL); #ifdef CONFIG_OASES_PERSIST_MEMORY static struct kobj_attribute attribute_boot = __ATTR(boot, S_IRUGO, boot_show, NULL); #endif static struct attribute *attrs[] = { &attribute_version.attr, &attribute_state.attr, &attribute_arch.attr, #ifdef CONFIG_OASES_PERSIST_MEMORY &attribute_boot.attr, #endif NULL }; static struct attribute_group attr_group = { .attrs = attrs, }; static struct kobj_attribute attribute_enabled = __ATTR(enabled, S_IWUSR | S_IRUGO, enabled_show, enabled_store); static struct kobj_attribute attribute_log = __ATTR(log, S_IRUGO, log_show, NULL); static struct kobj_attribute attribute_info = __ATTR(info, S_IRUGO, info_show, NULL); static struct attribute *attrs_patch[] = { &attribute_enabled.attr, &attribute_log.attr, &attribute_info.attr, NULL }; static void kobj_release_oases_patch(struct kobject *kobj) { struct oases_patch_info *ctx; ctx = container_of(kobj, struct oases_patch_info, kobj); oases_free_patch(ctx); } static struct kobj_type ktype_oases_patch = { .release = kobj_release_oases_patch, .sysfs_ops = &kobj_sysfs_ops, .default_attrs = attrs_patch }; void oases_sysfs_init_patch(struct oases_patch_info *info) { kobject_init(&info->kobj, &ktype_oases_patch); } int oases_sysfs_add_patch(struct oases_patch_info *info) { return kobject_add(&info->kobj, sysfs_oases, "%s", info->id); } void oases_sysfs_del_patch(struct oases_patch_info *info) { kobject_put(&info->kobj); } int __init oases_sysfs_init(void) { int ret; sysfs_oases = kobject_create_and_add(SYSFS_OASES, kernel_kobj); if (!sysfs_oases) { return -ENOMEM; } ret = sysfs_create_group(sysfs_oases, &attr_group); if (ret) goto create_group_fail; return 0; create_group_fail: kobject_put(sysfs_oases); return ret; } void oases_sysfs_destroy(void) { sysfs_remove_group(sysfs_oases, &attr_group); kobject_put(sysfs_oases); }