Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67944024 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sound fixes from Takashi Iwai:
 "It's our tradition to get a high volume of fixes late at rc7: this
  time, X32 ABI breakage was found and this resulted in a high number
  LOCs.  The necessary changes to ALSA core codes were fairly
  straightforward, and more importantly, they are specific to X32, thus
  should be safe to apply.

  Other than that, rather a collection of small fixes:
   - Removal of the code that blocks too long at closing the OSS
     sequencer client (which was spotted by syzkaller, unsurprisingly)
   - Fixes races at HD-audio HDMI i915 audio binding
   - a few HDSP/HDPM zero-division fixes
   - Quirks for HD-audio and USB-audio as usual"

* tag 'sound-4.5-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: hda - hdmi defer to register acomp eld notifier
  ALSA: hda - hdmi add wmb barrier for audio component
  ALSA: hda - Fix mic issues on Acer Aspire E1-472
  ALSA: seq: oss: Don't drain at closing a client
  ALSA: usb-audio: Add a quirk for Plantronics DA45
  ALSA: hdsp: Fix wrong boolean ctl value accesses
  ALSA: hdspm: Fix zero-division
  ALSA: hdspm: Fix wrong boolean ctl value accesses
  ALSA: timer: Fix ioctls for X32 ABI
  ALSA: timer: Fix broken compat timer user status ioctl
  ALSA: rawmidi: Fix ioctls X32 ABI
  ALSA: rawmidi: Use comapt_put_timespec()
  ALSA: pcm: Fix ioctls for X32 ABI
  ALSA: ctl: Fix ioctls for X32 ABI
parents 40fea2ed 790b415c
Loading
Loading
Loading
Loading
+74 −16
Original line number Diff line number Diff line
@@ -170,6 +170,19 @@ struct snd_ctl_elem_value32 {
        unsigned char reserved[128];
};

#ifdef CONFIG_X86_X32
/* x32 has a different alignment for 64bit values from ia32 */
struct snd_ctl_elem_value_x32 {
	struct snd_ctl_elem_id id;
	unsigned int indirect;	/* bit-field causes misalignment */
	union {
		s32 integer[128];
		unsigned char data[512];
		s64 integer64[64];
	} value;
	unsigned char reserved[128];
};
#endif /* CONFIG_X86_X32 */

/* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
@@ -219,9 +232,11 @@ static int get_elem_size(int type, int count)

static int copy_ctl_value_from_user(struct snd_card *card,
				    struct snd_ctl_elem_value *data,
				    struct snd_ctl_elem_value32 __user *data32,
				    void __user *userdata,
				    void __user *valuep,
				    int *typep, int *countp)
{
	struct snd_ctl_elem_value32 __user *data32 = userdata;
	int i, type, size;
	int uninitialized_var(count);
	unsigned int indirect;
@@ -239,8 +254,9 @@ static int copy_ctl_value_from_user(struct snd_card *card,
	if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
	    type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
		for (i = 0; i < count; i++) {
			s32 __user *intp = valuep;
			int val;
			if (get_user(val, &data32->value.integer[i]))
			if (get_user(val, &intp[i]))
				return -EFAULT;
			data->value.integer.value[i] = val;
		}
@@ -250,8 +266,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
			dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
			return -EINVAL;
		}
		if (copy_from_user(data->value.bytes.data,
				   data32->value.data, size))
		if (copy_from_user(data->value.bytes.data, valuep, size))
			return -EFAULT;
	}

@@ -261,7 +276,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
}

/* restore the value to 32bit */
static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
static int copy_ctl_value_to_user(void __user *userdata,
				  void __user *valuep,
				  struct snd_ctl_elem_value *data,
				  int type, int count)
{
@@ -270,22 +286,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
	if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
	    type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
		for (i = 0; i < count; i++) {
			s32 __user *intp = valuep;
			int val;
			val = data->value.integer.value[i];
			if (put_user(val, &data32->value.integer[i]))
			if (put_user(val, &intp[i]))
				return -EFAULT;
		}
	} else {
		size = get_elem_size(type, count);
		if (copy_to_user(data32->value.data,
				 data->value.bytes.data, size))
		if (copy_to_user(valuep, data->value.bytes.data, size))
			return -EFAULT;
	}
	return 0;
}

static int snd_ctl_elem_read_user_compat(struct snd_card *card, 
					 struct snd_ctl_elem_value32 __user *data32)
static int ctl_elem_read_user(struct snd_card *card,
			      void __user *userdata, void __user *valuep)
{
	struct snd_ctl_elem_value *data;
	int err, type, count;
@@ -294,7 +310,9 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
	if (data == NULL)
		return -ENOMEM;

	if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
	err = copy_ctl_value_from_user(card, data, userdata, valuep,
				       &type, &count);
	if (err < 0)
		goto error;

	snd_power_lock(card);
@@ -303,14 +321,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
		err = snd_ctl_elem_read(card, data);
	snd_power_unlock(card);
	if (err >= 0)
		err = copy_ctl_value_to_user(data32, data, type, count);
		err = copy_ctl_value_to_user(userdata, valuep, data,
					     type, count);
 error:
	kfree(data);
	return err;
}

static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
					  struct snd_ctl_elem_value32 __user *data32)
static int ctl_elem_write_user(struct snd_ctl_file *file,
			       void __user *userdata, void __user *valuep)
{
	struct snd_ctl_elem_value *data;
	struct snd_card *card = file->card;
@@ -320,7 +339,9 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
	if (data == NULL)
		return -ENOMEM;

	if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
	err = copy_ctl_value_from_user(card, data, userdata, valuep,
				       &type, &count);
	if (err < 0)
		goto error;

	snd_power_lock(card);
@@ -329,12 +350,39 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
		err = snd_ctl_elem_write(card, file, data);
	snd_power_unlock(card);
	if (err >= 0)
		err = copy_ctl_value_to_user(data32, data, type, count);
		err = copy_ctl_value_to_user(userdata, valuep, data,
					     type, count);
 error:
	kfree(data);
	return err;
}

static int snd_ctl_elem_read_user_compat(struct snd_card *card,
					 struct snd_ctl_elem_value32 __user *data32)
{
	return ctl_elem_read_user(card, data32, &data32->value);
}

static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
					  struct snd_ctl_elem_value32 __user *data32)
{
	return ctl_elem_write_user(file, data32, &data32->value);
}

#ifdef CONFIG_X86_X32
static int snd_ctl_elem_read_user_x32(struct snd_card *card,
				      struct snd_ctl_elem_value_x32 __user *data32)
{
	return ctl_elem_read_user(card, data32, &data32->value);
}

static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
				       struct snd_ctl_elem_value_x32 __user *data32)
{
	return ctl_elem_write_user(file, data32, &data32->value);
}
#endif /* CONFIG_X86_X32 */

/* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
				   struct snd_ctl_elem_info32 __user *data32,
@@ -393,6 +441,10 @@ enum {
	SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
	SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
	SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
#ifdef CONFIG_X86_X32
	SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
	SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
#endif /* CONFIG_X86_X32 */
};

static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -431,6 +483,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
		return snd_ctl_elem_add_compat(ctl, argp, 0);
	case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
		return snd_ctl_elem_add_compat(ctl, argp, 1);
#ifdef CONFIG_X86_X32
	case SNDRV_CTL_IOCTL_ELEM_READ_X32:
		return snd_ctl_elem_read_user_x32(ctl->card, argp);
	case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
		return snd_ctl_elem_write_user_x32(ctl, argp);
#endif /* CONFIG_X86_X32 */
	}

	down_read(&snd_ioctl_rwsem);
+176 −1
Original line number Diff line number Diff line
@@ -183,6 +183,14 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
	return err;
}

#ifdef CONFIG_X86_X32
/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
				     struct snd_pcm_channel_info __user *src);
#define snd_pcm_ioctl_channel_info_x32(s, p)	\
	snd_pcm_channel_info_user(s, p)
#endif /* CONFIG_X86_X32 */

struct snd_pcm_status32 {
	s32 state;
	struct compat_timespec trigger_tstamp;
@@ -243,6 +251,71 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
	return err;
}

#ifdef CONFIG_X86_X32
/* X32 ABI has 64bit timespec and 64bit alignment */
struct snd_pcm_status_x32 {
	s32 state;
	u32 rsvd; /* alignment */
	struct timespec trigger_tstamp;
	struct timespec tstamp;
	u32 appl_ptr;
	u32 hw_ptr;
	s32 delay;
	u32 avail;
	u32 avail_max;
	u32 overrange;
	s32 suspended_state;
	u32 audio_tstamp_data;
	struct timespec audio_tstamp;
	struct timespec driver_tstamp;
	u32 audio_tstamp_accuracy;
	unsigned char reserved[52-2*sizeof(struct timespec)];
} __packed;

#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))

static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
				   struct snd_pcm_status_x32 __user *src,
				   bool ext)
{
	struct snd_pcm_status status;
	int err;

	memset(&status, 0, sizeof(status));
	/*
	 * with extension, parameters are read/write,
	 * get audio_tstamp_data from user,
	 * ignore rest of status structure
	 */
	if (ext && get_user(status.audio_tstamp_data,
				(u32 __user *)(&src->audio_tstamp_data)))
		return -EFAULT;
	err = snd_pcm_status(substream, &status);
	if (err < 0)
		return err;

	if (clear_user(src, sizeof(*src)))
		return -EFAULT;
	if (put_user(status.state, &src->state) ||
	    put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
	    put_timespec(&status.tstamp, &src->tstamp) ||
	    put_user(status.appl_ptr, &src->appl_ptr) ||
	    put_user(status.hw_ptr, &src->hw_ptr) ||
	    put_user(status.delay, &src->delay) ||
	    put_user(status.avail, &src->avail) ||
	    put_user(status.avail_max, &src->avail_max) ||
	    put_user(status.overrange, &src->overrange) ||
	    put_user(status.suspended_state, &src->suspended_state) ||
	    put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
	    put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
	    put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
	    put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
		return -EFAULT;

	return err;
}
#endif /* CONFIG_X86_X32 */

/* both for HW_PARAMS and HW_REFINE */
static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
					  int refine, 
@@ -469,6 +542,93 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
	return 0;
}

#ifdef CONFIG_X86_X32
/* X32 ABI has 64bit timespec and 64bit alignment */
struct snd_pcm_mmap_status_x32 {
	s32 state;
	s32 pad1;
	u32 hw_ptr;
	u32 pad2; /* alignment */
	struct timespec tstamp;
	s32 suspended_state;
	struct timespec audio_tstamp;
} __packed;

struct snd_pcm_mmap_control_x32 {
	u32 appl_ptr;
	u32 avail_min;
};

struct snd_pcm_sync_ptr_x32 {
	u32 flags;
	u32 rsvd; /* alignment */
	union {
		struct snd_pcm_mmap_status_x32 status;
		unsigned char reserved[64];
	} s;
	union {
		struct snd_pcm_mmap_control_x32 control;
		unsigned char reserved[64];
	} c;
} __packed;

static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
				      struct snd_pcm_sync_ptr_x32 __user *src)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	volatile struct snd_pcm_mmap_status *status;
	volatile struct snd_pcm_mmap_control *control;
	u32 sflags;
	struct snd_pcm_mmap_control scontrol;
	struct snd_pcm_mmap_status sstatus;
	snd_pcm_uframes_t boundary;
	int err;

	if (snd_BUG_ON(!runtime))
		return -EINVAL;

	if (get_user(sflags, &src->flags) ||
	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
	    get_user(scontrol.avail_min, &src->c.control.avail_min))
		return -EFAULT;
	if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
		err = snd_pcm_hwsync(substream);
		if (err < 0)
			return err;
	}
	status = runtime->status;
	control = runtime->control;
	boundary = recalculate_boundary(runtime);
	if (!boundary)
		boundary = 0x7fffffff;
	snd_pcm_stream_lock_irq(substream);
	/* FIXME: we should consider the boundary for the sync from app */
	if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
		control->appl_ptr = scontrol.appl_ptr;
	else
		scontrol.appl_ptr = control->appl_ptr % boundary;
	if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
		control->avail_min = scontrol.avail_min;
	else
		scontrol.avail_min = control->avail_min;
	sstatus.state = status->state;
	sstatus.hw_ptr = status->hw_ptr % boundary;
	sstatus.tstamp = status->tstamp;
	sstatus.suspended_state = status->suspended_state;
	sstatus.audio_tstamp = status->audio_tstamp;
	snd_pcm_stream_unlock_irq(substream);
	if (put_user(sstatus.state, &src->s.status.state) ||
	    put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
	    put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
	    put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
	    put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
	    put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
	    put_user(scontrol.avail_min, &src->c.control.avail_min))
		return -EFAULT;

	return 0;
}
#endif /* CONFIG_X86_X32 */

/*
 */
@@ -487,7 +647,12 @@ enum {
	SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
	SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
	SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),

#ifdef CONFIG_X86_X32
	SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
	SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
	SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
	SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
#endif /* CONFIG_X86_X32 */
};

static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -559,6 +724,16 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
		return snd_pcm_ioctl_rewind_compat(substream, argp);
	case SNDRV_PCM_IOCTL_FORWARD32:
		return snd_pcm_ioctl_forward_compat(substream, argp);
#ifdef CONFIG_X86_X32
	case SNDRV_PCM_IOCTL_STATUS_X32:
		return snd_pcm_status_user_x32(substream, argp, false);
	case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
		return snd_pcm_status_user_x32(substream, argp, true);
	case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
		return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
	case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
		return snd_pcm_ioctl_channel_info_x32(substream, argp);
#endif /* CONFIG_X86_X32 */
	}

	return -ENOIOCTLCMD;
+54 −2
Original line number Diff line number Diff line
@@ -85,8 +85,7 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
	if (err < 0)
		return err;

	if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
	    put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
	if (compat_put_timespec(&status.tstamp, &src->tstamp) ||
	    put_user(status.avail, &src->avail) ||
	    put_user(status.xruns, &src->xruns))
		return -EFAULT;
@@ -94,9 +93,58 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
	return 0;
}

#ifdef CONFIG_X86_X32
/* X32 ABI has 64bit timespec and 64bit alignment */
struct snd_rawmidi_status_x32 {
	s32 stream;
	u32 rsvd; /* alignment */
	struct timespec tstamp;
	u32 avail;
	u32 xruns;
	unsigned char reserved[16];
} __attribute__((packed));

#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))

static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
					struct snd_rawmidi_status_x32 __user *src)
{
	int err;
	struct snd_rawmidi_status status;

	if (rfile->output == NULL)
		return -EINVAL;
	if (get_user(status.stream, &src->stream))
		return -EFAULT;

	switch (status.stream) {
	case SNDRV_RAWMIDI_STREAM_OUTPUT:
		err = snd_rawmidi_output_status(rfile->output, &status);
		break;
	case SNDRV_RAWMIDI_STREAM_INPUT:
		err = snd_rawmidi_input_status(rfile->input, &status);
		break;
	default:
		return -EINVAL;
	}
	if (err < 0)
		return err;

	if (put_timespec(&status.tstamp, &src->tstamp) ||
	    put_user(status.avail, &src->avail) ||
	    put_user(status.xruns, &src->xruns))
		return -EFAULT;

	return 0;
}
#endif /* CONFIG_X86_X32 */

enum {
	SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
	SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
#ifdef CONFIG_X86_X32
	SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32),
#endif /* CONFIG_X86_X32 */
};

static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -115,6 +163,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
		return snd_rawmidi_ioctl_params_compat(rfile, argp);
	case SNDRV_RAWMIDI_IOCTL_STATUS32:
		return snd_rawmidi_ioctl_status_compat(rfile, argp);
#ifdef CONFIG_X86_X32
	case SNDRV_RAWMIDI_IOCTL_STATUS_X32:
		return snd_rawmidi_ioctl_status_x32(rfile, argp);
#endif /* CONFIG_X86_X32 */
	}
	return -ENOIOCTLCMD;
}
+0 −2
Original line number Diff line number Diff line
@@ -149,8 +149,6 @@ odev_release(struct inode *inode, struct file *file)
	if ((dp = file->private_data) == NULL)
		return 0;

	snd_seq_oss_drain_write(dp);

	mutex_lock(&register_mutex);
	snd_seq_oss_release(dp);
	mutex_unlock(&register_mutex);
+0 −1
Original line number Diff line number Diff line
@@ -127,7 +127,6 @@ int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int co
unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);

void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);

/* */
void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
Loading