Ryan Murphy <ryanfmur...@gmail.com> writes:
> Looked thru the diffs and it looks fine to me.
> Changing a lot of a integer/long arguments that were being read directly via 
> atoi / atol
> to be read as strings first, to give better error handling.
> 
> This looks good to go to me.  Reviewing this as "Ready for Committer".
> Thanks for the patch, Pierre!

I took a quick look through this and had a few thoughts:

* If we're taking the trouble to sanity-check the input, I think we should
also check for ERANGE (i.e., overflow).

* I concur with Robert that you might as well just test for "*endptr != '\0'"
instead of adding a strlen() call.

* Replicating fiddly little code sequences like this all over the place
makes me itch.  There's too much chance to get it wrong, and even more
risk of someone taking shortcuts.  It has to be not much more complicated
than using atoi(), or we'll just be doing this exercise again in a few
years.  So I'm thinking we need to introduce a convenience function,
perhaps located in src/common/, or else src/fe_utils/.

* Changing variables from int to long int is likely to have unpleasant
consequences on platforms where they're different; or at least, I don't
particularly feel like auditing the patch to ensure that that's not a
problem anywhere.  So I think we should not do that but just make the
convenience function return int, with a suitable overflow test for that
result size.  There's existing logic you can copy in
src/backend/nodes/read.c:

        errno = 0;
        val = strtol(token, &endptr, 10);
        if (endptr != token + length || errno == ERANGE
#ifdef HAVE_LONG_INT_64
        /* if long > 32 bits, check for overflow of int4 */
            || val != (long) ((int32) val)
#endif
            )
            ... bad integer ...

If there are places where we actually do want a long result, we
could have two convenience functions, one returning int and one long.


The exact form of the convenience function is up for grabs, but one
way we could do it is

bool pg_int_convert(const char *str, int *value)

(where a true result indicates a valid integer value).
Then typical usage would be like

                if (!pg_int_convert(optarg, &compresslevel) ||
                    compresslevel < 0 || compresslevel > 9)
                    ... complain-and-exit ...

There might be something to be said for folding the range checks
into the convenience function:

bool pg_int_convert(const char *str, int min, int max, int *value)

                if (!pg_int_convert(optarg, 0, 9, &compresslevel))
                    ... complain-and-exit ...

That way you could protect code like

                standby_message_timeout = atoi(optarg) * 1000;

fairly easily:

                if (!pg_int_convert(optarg, 0, INT_MAX/1000,
                                    &standby_message_timeout))
                    ... complain-and-exit ...
                standby_message_timeout *= 1000;

                        regards, tom lane


-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to