@@ -226,6 +226,7 @@ struct snd_pcm_ops {
struct snd_pcm_file {
struct snd_pcm_substream *substream;
int no_compat_mmap;
+ unsigned int perm; /* file descriptor permissions */
unsigned int user_pversion; /* supported protocol version */
};
@@ -571,6 +571,15 @@ enum {
#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
+#define SNDRV_PCM_PERM_MMAP (1<<0)
+#define SNDRV_PCM_PERM_MMAP_STATUS (1<<1)
+#define SNDRV_PCM_PERM_MMAP_CONTROL (1<<2)
+#define SNDRV_PCM_PERM_RW (1<<3)
+#define SNDRV_PCM_PERM_CONTROL (1<<4)
+#define SNDRV_PCM_PERM_STATUS (1<<5)
+#define SNDRV_PCM_PERM_SYNC (1<<6)
+#define SNDRV_PCM_PERM_MAX ((SNDRV_PCM_PERM_SYNC<<1)-1)
+
#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int)
#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
@@ -2455,6 +2455,7 @@ static int snd_pcm_open_file(struct file *file,
return -ENOMEM;
}
pcm_file->substream = substream;
+ pcm_file->perm = ~0;
if (substream->ref_count == 1)
substream->pcm_release = pcm_release_private;
file->private_data = pcm_file;
@@ -2850,10 +2851,11 @@ static int snd_pcm_anonymous_dup(struct file *file,
int flags;
struct file *nfile;
struct snd_pcm *pcm = substream->pcm;
+ struct snd_pcm_file *pcm_file;
if (get_user(perm, (int __user *)arg))
return -EFAULT;
- if (perm < 0)
+ if (perm < 0 || perm > SNDRV_PCM_PERM_MAX)
return -ENOSYS;
flags = file->f_flags & (O_ACCMODE | O_NONBLOCK);
flags |= O_APPEND | O_CLOEXEC;
@@ -2878,6 +2880,8 @@ static int snd_pcm_anonymous_dup(struct file *file,
err = snd_pcm_open_file(nfile, substream->pcm,
substream, substream->stream);
if (err >= 0) {
+ pcm_file = nfile->private_data;
+ pcm_file->perm = perm;
put_user(fd, (int __user *)arg);
return 0;
}
@@ -2889,6 +2893,73 @@ static int snd_pcm_anonymous_dup(struct file *file,
return err;
}
+static int snd_pcm_ioctl_check_perm(struct snd_pcm_file *pcm_file,
+ unsigned int cmd)
+{
+ if (pcm_file->perm == ~0)
+ return 1;
+ /*
+ * the setup, linking and anonymous dup is not allowed
+ * for the restricted file descriptors
+ */
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_PVERSION:
+ case SNDRV_PCM_IOCTL_INFO:
+ case SNDRV_PCM_IOCTL_USER_PVERSION:
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO:
+ return 1;
+ }
+ if (pcm_file->perm & SNDRV_PCM_PERM_CONTROL) {
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_PREPARE:
+ case SNDRV_PCM_IOCTL_RESET:
+ case SNDRV_PCM_IOCTL_START:
+ case SNDRV_PCM_IOCTL_XRUN:
+ case SNDRV_PCM_IOCTL_RESUME:
+ case SNDRV_PCM_IOCTL_DRAIN:
+ case SNDRV_PCM_IOCTL_DROP:
+ case SNDRV_PCM_IOCTL_PAUSE:
+ return 1;
+ default:
+ break;
+ }
+ }
+ if (pcm_file->perm & SNDRV_PCM_PERM_STATUS) {
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_STATUS:
+ case SNDRV_PCM_IOCTL_STATUS_EXT:
+ case SNDRV_PCM_IOCTL_DELAY:
+ return 1;
+ default:
+ break;
+ }
+ }
+ if (pcm_file->perm & SNDRV_PCM_PERM_SYNC) {
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_HWSYNC:
+ case SNDRV_PCM_IOCTL_SYNC_PTR:
+ case SNDRV_PCM_IOCTL_REWIND:
+ case SNDRV_PCM_IOCTL_FORWARD:
+ return 1;
+ default:
+ break;
+ }
+ }
+ if (pcm_file->perm & SNDRV_PCM_PERM_RW) {
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
+ case SNDRV_PCM_IOCTL_READI_FRAMES:
+ case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+ case SNDRV_PCM_IOCTL_READN_FRAMES:
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int snd_pcm_common_ioctl(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
@@ -2903,6 +2974,9 @@ static int snd_pcm_common_ioctl(struct file *file,
if (res < 0)
return res;
+ if (!snd_pcm_ioctl_check_perm(pcm_file, cmd))
+ return -EPERM;
+
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
@@ -3251,6 +3325,9 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file
struct vm_area_struct *area)
{
long size;
+ struct snd_pcm_file *pcm_file = file->private_data;
+ if (!(pcm_file->perm & SNDRV_PCM_PERM_MMAP_STATUS))
+ return -EPERM;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
size = area->vm_end - area->vm_start;
@@ -3287,6 +3364,9 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
struct vm_area_struct *area)
{
long size;
+ struct snd_pcm_file *pcm_file = file->private_data;
+ if (!(pcm_file->perm & SNDRV_PCM_PERM_MMAP_CONTROL))
+ return -EPERM;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
size = area->vm_end - area->vm_start;
@@ -3461,11 +3541,14 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
struct snd_pcm_runtime *runtime;
+ struct snd_pcm_file *pcm_file = file->private_data;
long size;
unsigned long offset;
size_t dma_bytes;
int err;
+ if (!(pcm_file->perm & SNDRV_PCM_PERM_MMAP))
+ return -EPERM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!(area->vm_flags & (VM_WRITE|VM_READ)))
return -EINVAL;
Create seven control bits to allow the various restrictions for the anonymous file descriptor. Signed-off-by: Jaroslav Kysela <perex@perex.cz> --- include/sound/pcm.h | 1 + include/uapi/sound/asound.h | 9 +++++ sound/core/pcm_native.c | 85 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 1 deletion(-)