From: Jianhua Bi Date: Wed, 24 Jan 2024 07:32:58 +0000 (+0900) Subject: maru: eeprom: fix crash issue when using eprom ioctl on AMD CPU X-Git-Tag: accepted/tizen/unified/20240201.165108~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3a866074e0e62eac0ce528b475b69cf76f5a05eb;p=sdk%2Femulator%2Femulator-kernel.git maru: eeprom: fix crash issue when using eprom ioctl on AMD CPU AMD CPU has user memory protection, so direct accessing user address causes memory segment fault. To avoid the crash, use copy_{to/from}_user() for user addresses. Change-Id: I11d88050bfdc712d762c72903e6ca34699b72963 Signed-off-by: Jianhua Bi Signed-off-by: Seung-Woo Kim --- diff --git a/drivers/maru/tv/maru_eeprom.c b/drivers/maru/tv/maru_eeprom.c index 08ca77b..c2e210dc 100644 --- a/drivers/maru/tv/maru_eeprom.c +++ b/drivers/maru/tv/maru_eeprom.c @@ -222,48 +222,84 @@ static ssize_t eeprom_read_file(unsigned char __user *buf, int len, loff_t pos) static int eeprom_usr_write(const char __user *buf, size_t count, unsigned long addr) { int ret = -EFAULT; + char *k_wbuf; eep_log("eep write.\n"); + if (!buf) { eep_err("Invalid User buffer.\n"); + return ret; } else if (count > 0) { if (addr + count > eeprom_size) { eep_err("overflow!!.\n"); + return ret; + } + + k_wbuf = kmalloc(count + 1, GFP_KERNEL); + if (!k_wbuf) { + eep_err("failed to allocate kernel wbuf!\n"); + return -ENOMEM; + } + + if(copy_from_user(k_wbuf, buf, count)) { + eep_err("copy from user failed!\n"); goto out_err; } - ret = eeprom_write_file(buf, count, addr); + + ret = eeprom_write_file(k_wbuf, count, addr); if (ret < 0) { eep_warn("Write failed.\n"); + goto out_err; } } else { eep_warn("inavalid count.\n"); + return ret; } out_err: + kfree(k_wbuf); return ret; } static ssize_t eeprom_usr_read(char __user *buf, size_t count, unsigned long addr) { int ret = -EFAULT; + char *k_rbuf; eep_log("eep read.\n"); + if (!buf) { eep_err("User buf has no memory allocation.\n"); + return ret; } else if (count > 0) { if (count + addr > eeprom_size) { eep_err("overflow!!.\n"); - goto out_err; + return ret; } - ret = eeprom_read_file(buf, count, addr); + + k_rbuf = kmalloc(count + 1, GFP_KERNEL); + if (!k_rbuf) { + eep_err("failed to allocate kernel rbuf!\n"); + return -ENOMEM; + } + + ret = eeprom_read_file(k_rbuf, count, addr); if (ret < 0) { eep_warn("read failed.\n"); + goto out_err; + } else { + if (copy_to_user(buf, k_rbuf, count)) { + eep_err("copy to user failed!\n"); + goto out_err; + } } } else { eep_warn("invalid count.\n"); + return ret; } out_err: + kfree(k_rbuf); return ret; } @@ -540,14 +576,46 @@ long eep_ioctl(struct file *fp, unsigned int cmd, unsigned long args) case EEPROM_WRITE_DATA: case EEPROM_READ_DATA: { - struct eeprom_io_pkt *pkt = (struct eeprom_io_pkt *)args; - if (cmd == EEPROM_WRITE_DATA) - ret = eeprom_usr_write(pkt->wbuf, pkt->size, pkt->addr); - else - ret = eeprom_usr_read(pkt->rbuf, pkt->size, pkt->addr); - if (ret < 0) { - eep_warn("failed handle eeprom data: %ld", ret); + struct eeprom_io_pkt eemprom_pkt; + + if (copy_from_user(&eemprom_pkt, + (struct eeprom_io_pkt *)args, + sizeof(struct eeprom_io_pkt))) { + eep_warn("eemprom_pkt failed copy_from_user\n"); + ret = -EFAULT; + goto out_unlock; } + + if (cmd == EEPROM_WRITE_DATA) { + if (!access_ok(VERIFY_READ, + (const char __user *)eemprom_pkt.wbuf, + eemprom_pkt.size)) { + eep_err("eemprom_pkt.wbuf access fail!\n"); + ret = -EFAULT; + goto out_unlock; + } else { + ret = eeprom_usr_write(eemprom_pkt.wbuf, + eemprom_pkt.size, + eemprom_pkt.addr); + if (ret < 0) + eep_warn("failed write eeprom data: %ld", ret); + } + } else { + if (!access_ok(VERIFY_WRITE, + (const char __user *)eemprom_pkt.rbuf, + eemprom_pkt.size)) { + eep_err("eemprom_pkt.rbuf access fail!\n"); + ret = -EFAULT; + goto out_unlock; + } else { + ret = eeprom_usr_read(eemprom_pkt.rbuf, + eemprom_pkt.size, + eemprom_pkt.addr); + if (ret < 0) + eep_warn("failed read eeprom data: %ld", ret); + } + } + } break; default: @@ -555,6 +623,8 @@ long eep_ioctl(struct file *fp, unsigned int cmd, unsigned long args) ret = -EFAULT; break; } + +out_unlock: mutex_unlock(&eep_mutex); return ret; }