maru: eeprom: fix crash issue when using eprom ioctl on AMD CPU 40/304840/1
authorJianhua Bi <jianhua.bi@samsung.com>
Wed, 24 Jan 2024 07:32:58 +0000 (16:32 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 24 Jan 2024 07:32:58 +0000 (16:32 +0900)
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 <jianhua.bi@samsung.com>
Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
drivers/maru/tv/maru_eeprom.c

index 08ca77b..c2e210d 100644 (file)
@@ -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;
 }