@@ -3497,16 +3497,19 @@ nmreq_copyin(struct nmreq_header *hdr, int nr_body_is_user)
34973497 }
34983498 opt_tab [opt -> nro_reqtype ] = opt ;
34993499
3500- /* copy the option body */
3501- optsz = nmreq_opt_size_by_type (opt -> nro_reqtype ,
3502- opt -> nro_size );
3503- /* check optsz and nro_size to avoid for possible integer overflows of rqsz */
3504- if ((optsz > NETMAP_REQ_MAXSIZE ) || (opt -> nro_size > NETMAP_REQ_MAXSIZE )
3505- || (rqsz + optsz > NETMAP_REQ_MAXSIZE )
3506- || (optsz > 0 && rqsz + optsz <= rqsz )) {
3507- error = EMSGSIZE ;
3508- goto out_restore ;
3509- }
3500+ /* copy the option body */
3501+ optsz = nmreq_opt_size_by_type (opt -> nro_reqtype ,
3502+ opt -> nro_size );
3503+ /* enforce exact option body size and overflow guards */
3504+ if ((optsz == 0 && opt -> nro_size != 0 ) || (opt -> nro_size != optsz )) {
3505+ error = EINVAL ;
3506+ goto out_restore ;
3507+ }
3508+ if ((optsz > NETMAP_REQ_MAXSIZE ) || (opt -> nro_size > NETMAP_REQ_MAXSIZE )
3509+ || (rqsz > NETMAP_REQ_MAXSIZE - optsz )) {
3510+ error = EMSGSIZE ;
3511+ goto out_restore ;
3512+ }
35103513 rqsz += optsz ;
35113514 if (optsz ) {
35123515 /* the option body follows the option header */
@@ -3584,10 +3587,15 @@ nmreq_copyout(struct nmreq_header *hdr, int rerror)
35843587 goto out ;
35853588 }
35863589
3587- /* copy the option body only if there was no error */
3588- if (!rerror && !src -> nro_status ) {
3589- optsz = nmreq_opt_size_by_type (src -> nro_reqtype ,
3590- src -> nro_size );
3590+ /* copy the option body only if there was no error */
3591+ if (!rerror && !src -> nro_status ) {
3592+ optsz = nmreq_opt_size_by_type (src -> nro_reqtype ,
3593+ src -> nro_size );
3594+ /* enforce symmetry for copyout */
3595+ if ((optsz == 0 && src -> nro_size != 0 ) || (src -> nro_size != optsz )) {
3596+ rerror = EINVAL ;
3597+ goto out ;
3598+ }
35913599 if (optsz ) {
35923600 error = copyout (src + 1 , dst + 1 , optsz );
35933601 if (error ) {
0 commit comments