[PATCH v3 1/3] POSIX Asynchronous I/O support: aio files

Mark Geisert mark@maxrnd.com
Wed Jul 18 05:55:00 GMT 2018


Corinna Vinschen wrote:
> Hey Mark,
>
> I just belatedly noticed a few problems in aiosuspend:
>
> On Jul 15 01:20, Mark Geisert wrote:
>> +static int
>> +aiosuspend (const struct aiocb *const aiolist[],
>> +         int nent, const struct timespec *timeout)
>> +{
>> +  /* Returns lowest list index of completed aios, else 'nent' if all completed.
>> +   * If none completed on entry, wait for interval specified by 'timeout'.
>> +   */
>> +  DWORD     msecs = 0;
>> +  int       res;
>> +  sigset_t  sigmask;
>> +  siginfo_t si;
>> +  DWORD     time0, time1;
>      ^^^^^^^^^^^^^^^^^^^^^^^
>      see below
>
>> +  struct timespec *to = (struct timespec *) timeout;
>> +
>> +  if (to)
>> +    msecs = (1000 * to->tv_sec) + ((to->tv_nsec + 999999) / 1000000);
>
> You're not checking the content of timeout for validity, tv_sec >= 0 and
> 0 <= tv_nsec <= 999999999.

Oops.  Before this stmt was added I was relying on sigtimedwait() to validate. 
But doing math on the values here does demand validation here.  I will fix.

> I'm not sure why you break the timespec down to msecs anyway.  The
> timespec value is ultimately used for a timer with 100ns resolution.
> Why not stick to 64 bit 100ns values instead?  They are used in a
> lot of places.

OK, will change.  I was using msecs because the code only cares whether it's 
zero or nonzero and I couldn't see wasting 8-byte values on that.  But 
GetTickCount64() motivates for the longer variables... So will be fixed.

> Last but not least, please don't use numbers like 1000 or 999999 or
> 1000000 when converting time values.  We have macros for that defined
> in hires.h:
>
>   /* # of nanosecs per second. */
>   #define NSPERSEC (1000000000LL)
>   /* # of 100ns intervals per second. */
>   #define NS100PERSEC (10000000LL)
>   /* # of microsecs per second. */
>   #define USPERSEC (1000000LL)
>   /* # of millisecs per second. */
>   #define MSPERSEC (1000L)

I used these in my signal.cc updates but somehow forgot this AIO stuff is now 
inside Cygwin DLL so can use the same defines.  Will fix.

>> +  [...]
>> +  time0 = GetTickCount ();
>> +  //XXX Is it possible have an empty signal mask ...
>
> No, because some signals are non-maskable.
>
>> +  //XXX ... and infinite timeout?
>
> Yes, if timeout is a NULL pointer.

My XXX concern was whether an app could get stuck here and not be abortable. 
But I take your comments to mean a non-maskable signal will break out of the 
sigtimedwait(), so e.g. Ctrl-C, or SIGTERM from outside, could interrupt the app.

>> +  res = sigtimedwait (&sigmask, &si, to);
>> +  if (res == -1)
>> +    return -1; /* Return with errno set by failed sigtimedwait() */
>> +  time1 = GetTickCount ();
>
> This is unsafe.  As a 32 bit function GetTickCount wraps around roughly
> every 49 days.  Use ULONGLONG GetTickCount64() instead.

OK, will fix.

>> +  /* Adjust timeout to account for time just waited */
>> +  msecs -= (time1 - time0);
>> +  if (msecs < 0)
>
> This can't happen then.

Right.

>> +  to->tv_sec = msecs / 1000;
>> +  to->tv_nsec = (msecs % 1000) * 1000000;
>
> Uh oh, you're changing caller values, despite timeout being const.
> `to' shouldn't be a pointer, but a local struct timespec instead.

I'll revisit this issue.  This internal aiosuspend() routine is called from both 
aio_suspend() and lio_listio().  Those two functions have conflicting 
protections on args passed to them and I had some trouble coming up with 
something that would compile cleanly.  As I say, I will look at this again.

..mark



More information about the Cygwin-patches mailing list