Skip to content

Commit 264e158

Browse files
committed
doio: make do_shmio() work with args beyond I32
- Extend mpos/msize to the full size_t/STRLEN range (whatever that is on the current platform). - Fully validate the range of all incoming values, including making sure that mpos+misze does not overflow (I think you used to be able to crash perl with this before). - Assert that the optype is either OP_SHMREAD or OP_SHMWRITE (because the code blindly performs a shmwrite() if optype is not OP_SHMREAD). - Switch to Perl_croak_nocontext because why not. Fixes #22895.
1 parent bbd7a0f commit 264e158

File tree

1 file changed

+61
-23
lines changed

1 file changed

+61
-23
lines changed

doio.c

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,38 +3359,75 @@ Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp)
33593359
PERL_ARGS_ASSERT_DO_SHMIO;
33603360
PERL_UNUSED_ARG(sp);
33613361

3362-
char *shm;
3363-
struct shmid_ds shmds;
3364-
const I32 id = SvIVx(*++mark);
3365-
SV * const mstr = *++mark;
3366-
const I32 mpos = SvIVx(*++mark);
3367-
const I32 msize = SvIVx(*++mark);
3362+
const IV iv_id = SvIVx(*++mark);
3363+
SV *const mstr = *++mark;
3364+
const IV iv_mpos = SvIVx(*++mark);
3365+
const IV iv_msize = SvIVx(*++mark);
3366+
3367+
/* must fit in int */
3368+
if (
3369+
iv_id < 0
3370+
|| (sizeof (IV) > sizeof (int) && iv_id > PERL_INT_MAX)
3371+
) {
3372+
SETERRNO(EINVAL,LIB_INVARG);
3373+
return -1;
3374+
}
3375+
const int id = iv_id;
33683376

3369-
SETERRNO(0,0);
3370-
if (shmctl(id, IPC_STAT, &shmds) == -1)
3377+
/* must fit in both size_t and STRLEN (a.k.a Size_t) */
3378+
if (
3379+
iv_mpos < 0
3380+
|| (sizeof (IV) > sizeof (size_t) && iv_mpos > (IV)SIZE_MAX)
3381+
|| (sizeof (IV) > sizeof (STRLEN) && iv_mpos > (IV)(STRLEN)-1)
3382+
) {
3383+
SETERRNO(EFAULT,SS_ACCVIO);
33713384
return -1;
3372-
if (mpos < 0 || msize < 0
3373-
|| (size_t)mpos + msize > (size_t)shmds.shm_segsz) {
3374-
SETERRNO(EFAULT,SS_ACCVIO); /* can't do as caller requested */
3385+
}
3386+
const size_t mpos = iv_mpos;
3387+
3388+
/* must fit in both size_t and STRLEN (a.k.a Size_t) */
3389+
if (
3390+
iv_msize < 0
3391+
|| (sizeof (IV) > sizeof (size_t) && iv_msize > (IV)SIZE_MAX)
3392+
|| (sizeof (IV) > sizeof (STRLEN) && iv_msize > (IV)(STRLEN)-1)
3393+
/* for shmread(), we need one extra byte for the NUL terminator */
3394+
|| (optype == OP_SHMREAD && (STRLEN)iv_msize > (STRLEN)-1 - 1)
3395+
) {
3396+
SETERRNO(EFAULT,SS_ACCVIO);
33753397
return -1;
33763398
}
3377-
if (id >= 0) {
3378-
shm = (char *)shmat(id, NULL, (optype == OP_SHMREAD) ? SHM_RDONLY : 0);
3379-
} else {
3380-
SETERRNO(EINVAL,LIB_INVARG);
3399+
const size_t msize = iv_msize;
3400+
3401+
if (SIZE_MAX - mpos < msize) {
3402+
/* overflow */
3403+
SETERRNO(EFAULT,SS_ACCVIO);
33813404
return -1;
33823405
}
3383-
if (shm == (char *)-1) /* I hate System V IPC, I really do */
3406+
const size_t mpos_end = mpos + msize;
3407+
3408+
SETERRNO(0,0);
3409+
3410+
struct shmid_ds shmds;
3411+
if (shmctl(id, IPC_STAT, &shmds) == -1)
33843412
return -1;
3413+
3414+
if (mpos_end > (size_t)shmds.shm_segsz) {
3415+
SETERRNO(EFAULT,SS_ACCVIO);
3416+
return -1;
3417+
}
3418+
3419+
char *const shm = (char *)shmat(id, NULL, (optype == OP_SHMREAD) ? SHM_RDONLY : 0);
3420+
if (shm == (char *)-1) /* I hate System V IPC, I really do */
3421+
return -1;
3422+
33853423
if (optype == OP_SHMREAD) {
3386-
char *mbuf;
3387-
/* suppress warning when reading into undef var (tchrist 3/Mar/00) */
33883424
SvGETMAGIC(mstr);
33893425
SvUPGRADE(mstr, SVt_PV);
3426+
/* suppress warning when reading into undef var (tchrist 3/Mar/00) */
33903427
if (! SvOK(mstr))
33913428
SvPVCLEAR(mstr);
33923429
SvPOK_only(mstr);
3393-
mbuf = SvGROW(mstr, (STRLEN)msize+1);
3430+
char *const mbuf = SvGROW(mstr, (STRLEN)msize+1);
33943431

33953432
Copy(shm + mpos, mbuf, msize, char);
33963433
SvCUR_set(mstr, msize);
@@ -3400,18 +3437,19 @@ Perl_do_shmio(pTHX_ I32 optype, SV **mark, SV **sp)
34003437
SvTAINTED_on(mstr);
34013438
}
34023439
else {
3403-
STRLEN len;
3440+
assert(optype == OP_SHMWRITE);
34043441

3405-
const char *mbuf = SvPVbyte(mstr, len);
3406-
const I32 n = ((I32)len > msize) ? msize : (I32)len;
3442+
STRLEN len;
3443+
const char *const mbuf = SvPVbyte(mstr, len);
3444+
const STRLEN n = (len > msize) ? msize : len;
34073445
Copy(mbuf, shm + mpos, n, char);
34083446
if (n < msize)
34093447
memzero(shm + mpos + n, msize - n);
34103448
}
34113449
return shmdt(shm);
34123450
#else
34133451
/* diag_listed_as: shm%s not implemented */
3414-
Perl_croak(aTHX_ "shm I/O not implemented");
3452+
Perl_croak_nocontext("shm I/O not implemented");
34153453
return -1;
34163454
#endif
34173455
}

0 commit comments

Comments
 (0)