PROPOSAL: Fixing the sys5 emulation bugs in shmfs

From: Alan Cox (alan@lxorguk.ukuu.org.uk)
Date: Sun Mar 12 2000 - 21:45:26 EST


This is one (untested until tomorrow) way of solving the major problems

It

1. Makes shmat/shmctl and friends work in a chrooted environment
        but not the shmfs.

2. Fixes the emulation bug in IPC_RMID which does not remove the ID
        but marks it to be removed when nattch hits zero (as per SYS5.3/4
        and *BSD systems)

It doesnt solve the rest of the chroot issue. In an ideal world the
shmfs would have one namespace per shmfs and a single one of them would
be mounted as the sys5 space. That would give controlled private shared
memory spaces to chrooted environments. That one however is a lot harder

--- ../linux.vanilla/ipc/shm.c Sat Mar 11 13:59:17 2000
+++ ipc/shm.c Mon Mar 13 01:42:07 2000
@@ -22,6 +22,8 @@
  * environment
  * 4) Read and write are not implemented (should they?)
  * 5) No special nodes are supported
+ * 6) It fails to follow the SYS5.4, BSD and older Linux API correctly
+ * and breaks many applications right now (netscape, doom, gimp, ..)
  */
 
 #include <linux/config.h>
@@ -71,6 +73,7 @@
         unsigned long shm_npages; /* size of segment (pages) */
         pte_t **shm_dir; /* ptr to arr of ptrs to frames */
         int id;
+ int destroyed; /* set if the final detach kills */
         union permap {
                 struct shmem {
                         time_t atime;
@@ -115,6 +118,7 @@
 static void killseg_core(struct shmid_kernel *shp, int doacc);
 static void shm_open (struct vm_area_struct *shmd);
 static void shm_close (struct vm_area_struct *shmd);
+static void shm_remove_name(int id);
 static struct page * shm_nopage(struct vm_area_struct *, unsigned long, int);
 static int shm_swapout(struct page *, struct file *);
 #ifdef CONFIG_PROC_FS
@@ -310,6 +314,25 @@
         return 0;
 }
 
+static struct dentry *shm_push_root(void)
+{
+ struct dentry *old,*new;
+ lock_kernel();
+ new=init_task_union.task->fs->root;
+ old=current->fs->root;
+ current->fs->root=dget(new);
+ unlock_kernel();
+ return old;
+}
+
+static void shm_pop_root(struct dentry *root)
+{
+ lock_kernel();
+ dput(current->fs->root);
+ current->fs->root=root;
+ unlock_kernel();
+}
+
 static void shm_put_super(struct super_block *sb)
 {
         struct super_block **p = &shm_sb;
@@ -696,9 +719,11 @@
 {
         struct shmid_kernel *shp;
         int err, id = 0;
+ static int count=0;
 
         if (!shm_sb) {
- printk ("shmget: shm filesystem not mounted\n");
+ if(count++<5)
+ printk(KERN_WARNING "shmget: shm filesystem not mounted\n");
                 return -EINVAL;
         }
 
@@ -886,9 +911,11 @@
         struct shm_setbuf setbuf;
         struct shmid_kernel *shp;
         int err, version;
+ static int count;
 
         if (!shm_sb) {
- printk ("shmctl: shm filesystem not mounted\n");
+ if(count++<5)
+ printk (KERN_WARNING "shmctl: shm filesystem not mounted\n");
                 return -EINVAL;
         }
 
@@ -1008,18 +1035,29 @@
         }
         case IPC_RMID:
         {
- char *name;
+ /*
+ * We cannot simply remove the file. The SVID states
+ * that the block remains until the last person
+ * detaches from it, then is deleted. A shmat() on
+ * an RMID segment is legal in SYS5.3-4, and *BSD,
+ * (ie everywhere).
+ *
+ * Instead we set a destroyed flag, and then blow
+ * the name away when the usage hits zero.
+ */
                 if ((shmid % SEQ_MULTIPLIER)== zero_id)
                         return -EINVAL;
- name = shm_getname(shmid);
- if (IS_ERR(name))
- return PTR_ERR(name);
- lock_kernel();
- err = do_unlink (name);
- unlock_kernel();
- putname (name);
- if (err == -ENOENT)
- err = -EINVAL;
+ shp = shm_lock(shmid);
+ if(shp==NULL)
+ return -EINVAL;
+ err=-EIDRM;
+ if(shm_checkid(shp,shmid))
+ goto out_unlock;
+ if(shp->nattch==0)
+ shm_remove_name(shmid);
+ else
+ shp->destroyed=1;
+ shm_unlock(shmid);
                 return err;
         }
 
@@ -1099,6 +1137,7 @@
         int err;
         int flags;
         char *name;
+ struct dentry *saved;
 
         if (!shm_sb || (shmid % SEQ_MULTIPLIER) == zero_id)
                 return -EINVAL;
@@ -1119,7 +1158,10 @@
         if (IS_ERR (name))
                 return PTR_ERR (name);
 
+ saved=shm_push_root();
         file = filp_open (name, O_RDWR, 0);
+ shm_pop_root(saved);
+
         putname (name);
         if (IS_ERR (file))
                 goto bad_file;
@@ -1147,6 +1189,21 @@
         shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino);
 }
 
+static void shm_remove_name(int id)
+{
+ char *name = shm_getname(id);
+ if (!IS_ERR(name))
+ {
+ struct dentry *saved;
+ lock_kernel();
+ saved=shm_push_chroot();
+ do_unlink (name);
+ shm_pop_chroot(saved);
+ unlock_kernel();
+ putname (name);
+ }
+}
+
 /*
  * remove the attach descriptor shmd.
  * free memory for segment if it is marked destroyed.
@@ -1164,7 +1221,14 @@
         shp->shm_lprid = current->pid;
         shp->shm_dtim = CURRENT_TIME;
         shp->shm_nattch--;
- shm_unlock(id);
+ if(shp->shm_nattch==0 && shp->destroyed)
+ {
+ shp->destroyed=0;
+ shm_remove_name(id);
+ shm_unlock(id);
+ }
+ else
+ shm_unlock(id);
 }
 
 /*

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Mar 15 2000 - 21:00:23 EST