Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lUobs-0007QP-Bh for pgsql-bugs@arkaria.postgresql.org; Fri, 09 Apr 2021 10:43:32 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lUobo-0004S0-C3 for pgsql-bugs@arkaria.postgresql.org; Fri, 09 Apr 2021 10:43:28 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lUoaY-0006Dd-BU for pgsql-bugs@lists.postgresql.org; Fri, 09 Apr 2021 10:42:10 +0000 Received: from mail-pg1-x52b.google.com ([2607:f8b0:4864:20::52b]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1lUoaV-0007Ka-Ec for pgsql-bugs@lists.postgresql.org; Fri, 09 Apr 2021 10:42:09 +0000 Received: by mail-pg1-x52b.google.com with SMTP id w10so3592977pgh.5 for ; Fri, 09 Apr 2021 03:42:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=wfvPYuIQ+6VkJzKjhou6XcT8bQZiuTTRgNFSgAvNrOw=; b=JSAI0YK7UdtT8M/d91yLZL0i9NUDU7UfReuOfvB/HOzIGmEBV4il8TYgkJkOiyK/po zpJZ97XMDx6Oy1rhid5D8i+nlRl4S+FcNktRtD71jehqeWGL8C+RbzLXcgRELiOyg69r nPmQZZz9Cjk+Fo5M9KXY83uZgyx8QTTFE1cs+EivHzUY+foZ0WIX/kGk9trVlOfcrTHN p6TI4ylQb5KOEteWupI+s6MWGVR/wck6qMkPTfFhS2XnFgMN1vTQsHReFId9KhJ+1dX8 Y7rizMsyhIQ0qYhP1tQK0y3HzyNVuiadm8ELu8DMhYCt88yNfiuDV/UK1RjAn0ZNnr5E 0iXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=wfvPYuIQ+6VkJzKjhou6XcT8bQZiuTTRgNFSgAvNrOw=; b=iUEbxe6ax47i54UHyqqQgKXXLmz2Mc2GsmxwM5w/N1KX70L2xDnGwbgbFk6EQ1fxZe foQ59gbLgJT1/vuz0aSbDzG5mcrdctaM/ZVNHFgFa3/4SFV39+mAMomk7CBHoH2A8V8w J0r7S8FcBZkxVCQ1dwndOEs9jBW+vNYPz37B/jePMUiWGsiX+v5+FdLhSzGh6LU+FtNY lG2740ZbToxYSwG6y59fdXKKuYGu70ugoI3jt+4VCZjKLGeAFRHVjt80FConmAtjPPsc sdvWY8lwzfAf52oURrgELPb49GFTPaw1qSog8EZX/d7ZzUndTR3PL0hPPjWhAi5s24lb mOww== X-Gm-Message-State: AOAM530z3pa66SlmRy/Jbepju6WolfxikluUdMlXHy2/xf9ObudQGVrM KsTykixmzk+9PzIHefSX3ZUpETQIclDwjQ== X-Google-Smtp-Source: ABdhPJyAN3vYsBAugA3MDCOIM4RKoLlContE8hKqFx0Z2AEK11v0+YEel5Mf4AwCV56rCM7Iag7BaA== X-Received: by 2002:a05:6a00:2cd:b029:1f4:c3db:4191 with SMTP id b13-20020a056a0002cdb02901f4c3db4191mr11523820pft.71.1617964925894; Fri, 09 Apr 2021 03:42:05 -0700 (PDT) Received: from nol (2001-b011-0008-9647-4c71-2a6f-a895-f450.dynamic-ip6.hinet.net. [2001:b011:8:9647:4c71:2a6f:a895:f450]) by smtp.gmail.com with ESMTPSA id g18sm2054436pfb.178.2021.04.09.03.42.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 09 Apr 2021 03:42:04 -0700 (PDT) Date: Fri, 9 Apr 2021 18:42:46 +0800 From: Julien Rouhaud To: Michael Paquier Cc: t.larionov@postgrespro.ru, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #16953: OOB access while converting "interval" to char Message-ID: <20210409104246.vkeef6rbyevq46f3@nol> References: <16953-f255a18f8c51f1d5@postgresql.org> <20210407120856.3rjxxnnzzxn74vyv@nol> <20210408110403.wr3d552xilqsx4us@nol> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="xgzd36pwpb7r5dov" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --xgzd36pwpb7r5dov Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Apr 08, 2021 at 08:37:21PM +0900, Michael Paquier wrote: > On Thu, Apr 08, 2021 at 07:04:03PM +0800, Julien Rouhaud wrote: > > I'm fine with it too, although I'd probably go with [-13, 13] just to make sure > > that there's isn't silly off-by-one mistake. > > Also, I guess that you'd just want to compile twice a modulo based on > MONTHS_PER_YEAR to get the correct positive position in each array. I'm not sure what you mean by that. We receive a pg_tm struct which can't contain more than 12 in tm_mon right? And actually for intervals it will reduce "12 months" to "1 year 0 month", so to_char previously didn't report anything for 12 months either. > > > I'll just wait a bit to see if anyone else has any opinion on whether -1 month > > should be January or December. > > Sure. If you can send an updated patch, that would be great. Hearing no other opinion I went with -1 -> december and so on in attached v2. I also fixed the "[-]12 months" case and updated the regression tests. Given the extra code needed to deduce the correct array position I factorized DCH_RM and DCH_rm. --xgzd36pwpb7r5dov Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v2-fix_rm.diff" diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 783c7b5e7a..80138d6347 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -3207,18 +3207,36 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col s += strlen(s); break; case DCH_RM: - if (!tm->tm_mon) - break; - sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4, - rm_months_upper[MONTHS_PER_YEAR - tm->tm_mon]); - s += strlen(s); - break; + /* FALLTHROUGH */ case DCH_rm: - if (!tm->tm_mon) + /* + * For intervals, values like '12 month' will be reduced to 0 + * month and some years, which we want to process. + */ + if (!tm->tm_mon && !tm->tm_year) break; - sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4, - rm_months_lower[MONTHS_PER_YEAR - tm->tm_mon]); - s += strlen(s); + else + { + int mon = tm->tm_mon; + const char * const* months; + + if (n->key->id == DCH_RM) + months = rm_months_upper; + else + months = rm_months_lower; + + if (mon == 0) + { + Assert(is_interval); + mon = tm->tm_year >= 0 ? MONTHS_PER_YEAR : 1; + } + else if (mon < 0) + mon = MONTHS_PER_YEAR + mon + 1; + + sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4, + months[MONTHS_PER_YEAR - mon]); + s += strlen(s); + } break; case DCH_W: sprintf(s, "%d", (tm->tm_mday - 1) / 7 + 1); diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index 690656dfb2..7c77fcae71 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -1907,6 +1907,39 @@ SELECT to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US') 7 78 789 7890 78901 789012 7 78 789 7890 78901 789012 789 789012 (4 rows) +SELECT i, to_char(i * interval '1mon', 'rm'), to_char(i * interval '1mon', 'RM') + FROM generate_series(-13, 13) i; + i | to_char | to_char +-----+---------+--------- + -13 | xii | XII + -12 | i | I + -11 | ii | II + -10 | iii | III + -9 | iv | IV + -8 | v | V + -7 | vi | VI + -6 | vii | VII + -5 | viii | VIII + -4 | ix | IX + -3 | x | X + -2 | xi | XI + -1 | xii | XII + 0 | | + 1 | i | I + 2 | ii | II + 3 | iii | III + 4 | iv | IV + 5 | v | V + 6 | vi | VI + 7 | vii | VII + 8 | viii | VIII + 9 | ix | IX + 10 | x | X + 11 | xi | XI + 12 | xii | XII + 13 | i | I +(27 rows) + -- timestamp numeric fields constructor SELECT make_timestamp(2014, 12, 28, 6, 30, 45.887); make_timestamp diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index c43a1f2268..3ae9a52347 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -319,6 +319,9 @@ SELECT to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US') ('2018-11-02 12:34:56.78901234') ) d(d); +SELECT i, to_char(i * interval '1mon', 'rm'), to_char(i * interval '1mon', 'RM') + FROM generate_series(-13, 13) i; + -- timestamp numeric fields constructor SELECT make_timestamp(2014, 12, 28, 6, 30, 45.887); SELECT make_timestamp(-44, 3, 15, 12, 30, 15); --xgzd36pwpb7r5dov--