diff options
Diffstat (limited to 'sound/synth/emux/soundfont.c')
-rw-r--r-- | sound/synth/emux/soundfont.c | 73 |
1 files changed, 55 insertions, 18 deletions
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c index 16f00097cb95..2373ed580bf8 100644 --- a/sound/synth/emux/soundfont.c +++ b/sound/synth/emux/soundfont.c @@ -689,6 +689,21 @@ find_sample(struct snd_soundfont *sf, int sample_id) } +static int +validate_sample_info(struct soundfont_sample_info *si) +{ + if (si->end < 0 || si->end > si->size) + return -EINVAL; + if (si->loopstart < 0 || si->loopstart > si->end) + return -EINVAL; + if (si->loopend < 0 || si->loopend > si->end) + return -EINVAL; + /* be sure loop points start < end */ + if (si->loopstart > si->loopend) + swap(si->loopstart, si->loopend); + return 0; +} + /* * Load sample information, this can include data to be loaded onto * the soundcard. It can also just be a pointer into soundcard ROM. @@ -701,7 +716,6 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) struct snd_soundfont *sf; struct soundfont_sample_info sample_info; struct snd_sf_sample *sp; - long off; /* patch must be opened */ sf = sflist->currsf; @@ -711,12 +725,16 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) if (is_special_type(sf->type)) return -EINVAL; + if (count < (long)sizeof(sample_info)) { + return -EINVAL; + } if (copy_from_user(&sample_info, data, sizeof(sample_info))) return -EFAULT; + data += sizeof(sample_info); + count -= sizeof(sample_info); - off = sizeof(sample_info); - - if (sample_info.size != (count-off)/2) + // SoundFont uses S16LE samples. + if (sample_info.size * 2 != count) return -EINVAL; /* Check for dup */ @@ -727,6 +745,21 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) return -EINVAL; } + if (sample_info.size > 0) { + if (sample_info.start < 0) + return -EINVAL; + + // Here we "rebase out" the start address, because the + // real start is the start of the provided sample data. + sample_info.end -= sample_info.start; + sample_info.loopstart -= sample_info.start; + sample_info.loopend -= sample_info.start; + sample_info.start = 0; + + if (validate_sample_info(&sample_info) < 0) + return -EINVAL; + } + /* Allocate a new sample structure */ sp = sf_sample_new(sflist, sf); if (!sp) @@ -735,7 +768,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) sp->v = sample_info; sp->v.sf_id = sf->id; sp->v.dummy = 0; - sp->v.truesize = sp->v.size; + sp->v.truesize = 0; /* * If there is wave data then load it. @@ -744,7 +777,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) int rc; rc = sflist->callback.sample_new (sflist->callback.private_data, sp, sflist->memhdr, - data + off, count - off); + data, count); if (rc < 0) { sf_sample_delete(sflist, sf, sp); return rc; @@ -941,8 +974,7 @@ int snd_sf_vol_table[128] = { /* load GUS patch */ static int -load_guspatch(struct snd_sf_list *sflist, const char __user *data, - long count, int client) +load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count) { struct patch_info patch; struct snd_soundfont *sf; @@ -957,10 +989,12 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, } if (copy_from_user(&patch, data, sizeof(patch))) return -EFAULT; - count -= sizeof(patch); data += sizeof(patch); + if ((patch.len << (patch.mode & WAVE_16_BITS ? 1 : 0)) != count) + return -EINVAL; + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); if (sf == NULL) return -ENOMEM; @@ -975,6 +1009,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, smp->v.loopend = patch.loop_end; smp->v.size = patch.len; + if (validate_sample_info(&smp->v) < 0) { + sf_sample_delete(sflist, sf, smp); + return -EINVAL; + } + /* set up mode flags */ smp->v.mode_flags = 0; if (!(patch.mode & WAVE_16_BITS)) @@ -1012,7 +1051,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, /* * load wave data */ - if (sflist->callback.sample_new) { + if (smp->v.size > 0) { rc = sflist->callback.sample_new (sflist->callback.private_data, smp, sflist->memhdr, data, count); @@ -1122,11 +1161,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, /* load GUS patch */ int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data, - long count, int client) + long count) { int rc; lock_preset(sflist); - rc = load_guspatch(sflist, data, count, client); + rc = load_guspatch(sflist, data, count); unlock_preset(sflist); return rc; } @@ -1377,9 +1416,8 @@ snd_sf_clear(struct snd_sf_list *sflist) } for (sp = sf->samples; sp; sp = nextsp) { nextsp = sp->next; - if (sflist->callback.sample_free) - sflist->callback.sample_free(sflist->callback.private_data, - sp, sflist->memhdr); + sflist->callback.sample_free(sflist->callback.private_data, + sp, sflist->memhdr); kfree(sp); } kfree(sf); @@ -1481,9 +1519,8 @@ snd_soundfont_remove_unlocked(struct snd_sf_list *sflist) nextsp = sp->next; sf->samples = nextsp; sflist->mem_used -= sp->v.truesize; - if (sflist->callback.sample_free) - sflist->callback.sample_free(sflist->callback.private_data, - sp, sflist->memhdr); + sflist->callback.sample_free(sflist->callback.private_data, + sp, sflist->memhdr); kfree(sp); } } |