Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolai Stange <nstange@suse.de>2019-04-09 11:52:18 +0200
committerNicolai Stange <nstange@suse.de>2019-04-09 11:52:18 +0200
commit8d591e0aa46f33b9dfe5e1245582e341d1babcaf (patch)
tree2f7c384040a0ba4a51c184047f72451289474402
parent708db993f6ee803ad0b4346f34189efe8a518d85 (diff)
parent1fe40709a6707864717784fff647fcfd45482713 (diff)
Merge branch 'bsc#1131390_12.3u12-15' into SLE12-SP3_Update_12SLE12-SP3_Update_12
-rw-r--r--bsc1131390/kgr_patch_bsc1131390.c298
-rw-r--r--bsc1131390/kgr_patch_bsc1131390.h17
2 files changed, 315 insertions, 0 deletions
diff --git a/bsc1131390/kgr_patch_bsc1131390.c b/bsc1131390/kgr_patch_bsc1131390.c
new file mode 100644
index 0000000..ca40531
--- /dev/null
+++ b/bsc1131390/kgr_patch_bsc1131390.c
@@ -0,0 +1,298 @@
+/*
+ * kgraft_patch_bsc1131390
+ *
+ * Fix for CVE-2018-14734, bsc#1131390
+ *
+ * Upstream commit:
+ * cb2595c1393b ("infiniband: fix a possible use-after-free bug")
+ *
+ * SLE12(-SP1) commit:
+ * 5bfcd007ba127a6b3becdfd2bc28943742251bf0
+ *
+ * SLE12-SP2 commit:
+ * a652ed6855a0544560af4c00435cc60c160399e8
+ *
+ * SLE12-SP3 commit:
+ * a652ed6855a0544560af4c00435cc60c160399e8
+ *
+ * SLE12-SP4 commit:
+ * d7808ffba4bbfa4fec4c99f0c9ac410a4fc580b8
+ *
+ * SLE15 commit:
+ * d7808ffba4bbfa4fec4c99f0c9ac410a4fc580b8
+ *
+ *
+ * Copyright (c) 2019 SUSE
+ * Author: Nicolai Stange <nstange@suse.de>
+ *
+ * Based on the original Linux kernel code. Other copyrights apply.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <uapi/rdma/rdma_user_cm.h>
+#include <rdma/ib_sa.h>
+#include <rdma/rdma_cm.h>
+#include "kgr_patch_bsc1131390.h"
+#include "kallsyms_relocs.h"
+
+#if !IS_MODULE(CONFIG_INFINIBAND_USER_ACCESS)
+#error "Live patch supports only CONFIG_INFINIBAND_USER_ACCESS=m"
+#endif
+
+#if !IS_ENABLED(CONFIG_INFINIBAND_ADDR_TRANS)
+#error "Live patch supports only CONFIG_INFINIBAND_ADDR_TRANS=y"
+#endif
+
+#if !IS_MODULE(CONFIG_INFINIBAND)
+#error "Live patch supports only CONFIG_INFINIBAND=m"
+#endif
+
+#define KGR_PATCHED_MODULE "rdma_ucm"
+
+
+static struct mutex (*kgr_mut);
+static struct idr (*kgr_multicast_idr);
+
+struct ucma_file;
+struct ucma_context;
+struct ucma_multicast;
+
+static struct ucma_context *(*kgr_ucma_get_ctx)(struct ucma_file *file, int id);
+static void (*kgr_ucma_put_ctx)(struct ucma_context *ctx);
+static void (*kgr_ucma_cleanup_mc_events)(struct ucma_multicast *mc);
+
+static int (*kgr_rdma_join_multicast)(struct rdma_cm_id *id,
+ struct sockaddr *addr,
+ u8 join_state, void *context);
+static void (*kgr_rdma_leave_multicast)(struct rdma_cm_id *id,
+ struct sockaddr *addr);
+
+static int (*kgr_rdma_addr_size)(struct sockaddr *addr);
+
+static struct kgr_kallsyms_reloc kgr_funcs[] = {
+ { "mut", (void *)&kgr_mut, "rdma_ucm" },
+ { "multicast_idr", (void *)&kgr_multicast_idr, "rdma_ucm" },
+ { "ucma_get_ctx", (void *)&kgr_ucma_get_ctx, "rdma_ucm" },
+ { "ucma_put_ctx", (void *)&kgr_ucma_put_ctx, "rdma_ucm" },
+ { "ucma_cleanup_mc_events", (void *)&kgr_ucma_cleanup_mc_events,
+ "rdma_ucm" },
+ { "rdma_join_multicast", (void *)&kgr_rdma_join_multicast, "rdma_cm" },
+ { "rdma_leave_multicast", (void *)&kgr_rdma_leave_multicast,
+ "rdma_cm" },
+ { "rdma_addr_size", (void *)&kgr_rdma_addr_size, "ib_core" },
+};
+
+
+/* from drivers/infiniband/core/ucma.c */
+struct ucma_file {
+ struct mutex mut;
+ struct file *filp;
+ struct list_head ctx_list;
+ struct list_head event_list;
+ wait_queue_head_t poll_wait;
+ struct workqueue_struct *close_wq;
+};
+
+struct ucma_context {
+ int id;
+ struct completion comp;
+ atomic_t ref;
+ int events_reported;
+ int backlog;
+
+ struct ucma_file *file;
+ struct rdma_cm_id *cm_id;
+ u64 uid;
+
+ struct list_head list;
+ struct list_head mc_list;
+ /* mark that device is in process of destroying the internal HW
+ * resources, protected by the global mut
+ */
+ int closing;
+ /* sync between removal event and id destroy, protected by file mut */
+ int destroying;
+ struct work_struct close_work;
+};
+
+struct ucma_multicast {
+ struct ucma_context *ctx;
+ int id;
+ int events_reported;
+
+ u64 uid;
+ u8 join_state;
+ struct list_head list;
+ struct sockaddr_storage addr;
+};
+
+
+
+/* patched, inlined */
+static struct ucma_multicast* kgr_ucma_alloc_multicast(struct ucma_context *ctx)
+{
+ struct ucma_multicast *mc;
+
+ mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return NULL;
+
+ mutex_lock(&(*kgr_mut));
+ /*
+ * Fix CVE-2018-14734
+ * -1 line, +1 line
+ */
+ mc->id = idr_alloc(&(*kgr_multicast_idr), NULL, 0, 0, GFP_KERNEL);
+ mutex_unlock(&(*kgr_mut));
+ if (mc->id < 0)
+ goto error;
+
+ mc->ctx = ctx;
+ list_add_tail(&mc->list, &ctx->mc_list);
+ return mc;
+
+error:
+ kfree(mc);
+ return NULL;
+}
+
+/* patched */
+ssize_t kgr_ucma_process_join(struct ucma_file *file,
+ struct rdma_ucm_join_mcast *cmd, int out_len)
+{
+ struct rdma_ucm_create_id_resp resp;
+ struct ucma_context *ctx;
+ struct ucma_multicast *mc;
+ struct sockaddr *addr;
+ int ret;
+ u8 join_state;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ addr = (struct sockaddr *) &cmd->addr;
+ if (cmd->addr_size != kgr_rdma_addr_size(addr))
+ return -EINVAL;
+
+ if (cmd->join_flags == RDMA_MC_JOIN_FLAG_FULLMEMBER)
+ join_state = BIT(FULLMEMBER_JOIN);
+ else if (cmd->join_flags == RDMA_MC_JOIN_FLAG_SENDONLY_FULLMEMBER)
+ join_state = BIT(SENDONLY_FULLMEMBER_JOIN);
+ else
+ return -EINVAL;
+
+ ctx = kgr_ucma_get_ctx(file, cmd->id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ mutex_lock(&file->mut);
+ mc = kgr_ucma_alloc_multicast(ctx);
+ if (!mc) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ mc->join_state = join_state;
+ mc->uid = cmd->uid;
+ memcpy(&mc->addr, addr, cmd->addr_size);
+ ret = kgr_rdma_join_multicast(ctx->cm_id, (struct sockaddr *)&mc->addr,
+ join_state, mc);
+ if (ret)
+ goto err2;
+
+ resp.id = mc->id;
+ if (copy_to_user((void __user *)(unsigned long) cmd->response,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err3;
+ }
+
+ /*
+ * Fix CVE-2018-14734
+ * +4 lines
+ */
+ mutex_lock(&(*kgr_mut));
+ idr_replace(&(*kgr_multicast_idr), mc, mc->id);
+ mutex_unlock(&(*kgr_mut));
+
+ mutex_unlock(&file->mut);
+ kgr_ucma_put_ctx(ctx);
+ return 0;
+
+err3:
+ kgr_rdma_leave_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr);
+ kgr_ucma_cleanup_mc_events(mc);
+err2:
+ mutex_lock(&(*kgr_mut));
+ idr_remove(&(*kgr_multicast_idr), mc->id);
+ mutex_unlock(&(*kgr_mut));
+ list_del(&mc->list);
+ kfree(mc);
+err1:
+ mutex_unlock(&file->mut);
+ kgr_ucma_put_ctx(ctx);
+ return ret;
+}
+
+
+
+static int kgr_patch_bsc1131390_module_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct module *mod = data;
+ int ret;
+
+ if (action != MODULE_STATE_COMING || strcmp(mod->name, KGR_PATCHED_MODULE))
+ return 0;
+
+ ret = __kgr_resolve_kallsyms_relocs(kgr_funcs, ARRAY_SIZE(kgr_funcs));
+ WARN(ret, "kgraft-patch: delayed kallsyms lookup failed. System is broken and can crash.\n");
+
+ return ret;
+}
+
+static struct notifier_block kgr_patch_bsc1131390_module_nb = {
+ .notifier_call = kgr_patch_bsc1131390_module_notify,
+ .priority = INT_MIN+1,
+};
+
+int kgr_patch_bsc1131390_init(void)
+{
+ int ret;
+
+ mutex_lock(&module_mutex);
+ if (find_module(KGR_PATCHED_MODULE)) {
+ ret = __kgr_resolve_kallsyms_relocs(kgr_funcs,
+ ARRAY_SIZE(kgr_funcs));
+ if (ret)
+ goto out;
+ }
+
+ ret = register_module_notifier(&kgr_patch_bsc1131390_module_nb);
+out:
+ mutex_unlock(&module_mutex);
+ return ret;
+}
+
+void kgr_patch_bsc1131390_cleanup(void)
+{
+ unregister_module_notifier(&kgr_patch_bsc1131390_module_nb);
+}
diff --git a/bsc1131390/kgr_patch_bsc1131390.h b/bsc1131390/kgr_patch_bsc1131390.h
new file mode 100644
index 0000000..939ee53
--- /dev/null
+++ b/bsc1131390/kgr_patch_bsc1131390.h
@@ -0,0 +1,17 @@
+#ifndef _KGR_PATCH_BSC1131390_H
+#define _KGR_PATCH_BSC1131390_H
+
+int kgr_patch_bsc1131390_init(void);
+void kgr_patch_bsc1131390_cleanup(void);
+
+
+struct ucma_file;
+struct rdma_ucm_join_mcast;
+
+ssize_t kgr_ucma_process_join(struct ucma_file *file,
+ struct rdma_ucm_join_mcast *cmd, int out_len);
+
+#define KGR_PATCH_BSC1131390_FUNCS \
+ KGR_PATCH_OBJ(ucma_process_join, kgr_ucma_process_join, "rdma_ucm"),
+
+#endif /* _KGR_PATCH_BSC1131390_H */