From a2d8c3d5a0cdbd8d2f47ef06cafdab5f76dda604 Mon Sep 17 00:00:00 2001 From: "Mikhail A. Kulagin" Date: Tue, 17 Nov 2020 13:01:50 +0300 Subject: [PATCH] Fix CVE-2020-14350 --- .gitignore | 1 - Makefile | 11 ++--- aqo--1.1.sql | 65 ++++++++++++++++++++++++++++ expected/aqo_CVE-2020-14350.out | 76 +++++++++++++++++++++++++++++++++ sql/aqo_CVE-2020-14350.sql | 71 ++++++++++++++++++++++++++++++ t/000_security.pl | 43 ------------------- 6 files changed, 215 insertions(+), 52 deletions(-) create mode 100644 aqo--1.1.sql create mode 100644 expected/aqo_CVE-2020-14350.out create mode 100644 sql/aqo_CVE-2020-14350.sql delete mode 100644 t/000_security.pl diff --git a/.gitignore b/.gitignore index bb52af91..e2fcd401 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,3 @@ regression.out *.gcov tags -aqo--?.?.sql diff --git a/Makefile b/Makefile index ada73ac9..5c4fb02d 100644 --- a/Makefile +++ b/Makefile @@ -13,14 +13,12 @@ REGRESS = aqo_disabled \ aqo_intelligent \ aqo_forced \ aqo_learn \ - schema + schema \ + aqo_CVE-2020-14350 EXTRA_REGRESS_OPTS=--temp-config=$(top_srcdir)/$(subdir)/conf.add -DATA = aqo--1.0.sql aqo--1.0--1.1.sql -DATA_built = aqo--1.1.sql - -TAP_TESTS = 1 +DATA = aqo--1.0.sql aqo--1.0--1.1.sql aqo--1.1.sql MODULE_big = aqo ifdef USE_PGXS @@ -34,6 +32,3 @@ include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif - -$(DATA_built): $(DATA) - cat $+ > $@ diff --git a/aqo--1.1.sql b/aqo--1.1.sql new file mode 100644 index 00000000..9c8a8227 --- /dev/null +++ b/aqo--1.1.sql @@ -0,0 +1,65 @@ +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION aqo" to load this file. \quit + +CREATE TABLE public.aqo_queries ( + query_hash int CONSTRAINT aqo_queries_query_hash_idx PRIMARY KEY, + learn_aqo boolean NOT NULL, + use_aqo boolean NOT NULL, + fspace_hash int NOT NULL, + auto_tuning boolean NOT NULL +); + +CREATE TABLE public.aqo_query_texts ( + query_hash int CONSTRAINT aqo_query_texts_query_hash_idx PRIMARY KEY REFERENCES public.aqo_queries ON DELETE CASCADE, + query_text text NOT NULL +); + +CREATE TABLE public.aqo_query_stat ( + query_hash int CONSTRAINT aqo_query_stat_idx PRIMARY KEY REFERENCES public.aqo_queries ON DELETE CASCADE, + execution_time_with_aqo double precision[], + execution_time_without_aqo double precision[], + planning_time_with_aqo double precision[], + planning_time_without_aqo double precision[], + cardinality_error_with_aqo double precision[], + cardinality_error_without_aqo double precision[], + executions_with_aqo bigint, + executions_without_aqo bigint +); + +CREATE TABLE public.aqo_data ( + fspace_hash int NOT NULL REFERENCES public.aqo_queries ON DELETE CASCADE, + fsspace_hash int NOT NULL, + nfeatures int NOT NULL, + features double precision[][], + targets double precision[], + UNIQUE (fspace_hash, fsspace_hash) +); + +CREATE UNIQUE INDEX aqo_fss_access_idx ON public.aqo_data (fspace_hash, fsspace_hash); + +ALTER TABLE public.aqo_data ALTER COLUMN features SET STORAGE MAIN; +ALTER TABLE public.aqo_data ALTER COLUMN targets SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN execution_time_with_aqo SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN execution_time_without_aqo SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN planning_time_with_aqo SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN planning_time_without_aqo SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN cardinality_error_without_aqo SET STORAGE MAIN; +ALTER TABLE public.aqo_query_stat +ALTER COLUMN cardinality_error_with_aqo SET STORAGE MAIN; + +INSERT INTO public.aqo_queries VALUES (0, false, false, 0, false); +INSERT INTO public.aqo_query_texts VALUES (0, 'COMMON feature space (do not delete!)'); +-- a virtual query for COMMON feature space + +CREATE FUNCTION invalidate_deactivated_queries_cache() RETURNS trigger + AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE TRIGGER aqo_queries_invalidate AFTER UPDATE OR DELETE OR TRUNCATE + ON public.aqo_queries FOR EACH STATEMENT + EXECUTE PROCEDURE invalidate_deactivated_queries_cache(); + diff --git a/expected/aqo_CVE-2020-14350.out b/expected/aqo_CVE-2020-14350.out new file mode 100644 index 00000000..cb8ed2b7 --- /dev/null +++ b/expected/aqo_CVE-2020-14350.out @@ -0,0 +1,76 @@ +/* + * Check fix for CVE-2020-14350. + * See also 7eeb1d986 postgresql commit. + */ +SET client_min_messages = 'warning'; +DROP ROLE IF EXISTS regress_hacker; +SET client_min_messages = 'notice'; +CREATE ROLE regress_hacker LOGIN; +-- Test 1 +RESET ROLE; +ALTER ROLE regress_hacker NOSUPERUSER; +SET ROLE regress_hacker; +SHOW is_superuser; + is_superuser +-------------- + off +(1 row) + +CREATE FUNCTION format(f text, r regclass, t text) +RETURNS text +AS $$ +BEGIN + ALTER ROLE regress_hacker SUPERUSER; + RETURN ''; +END +$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; +RESET ROLE; +CREATE EXTENSION aqo; +-- Test result (must be 'off') +SET ROLE regress_hacker; +SHOW is_superuser; + is_superuser +-------------- + off +(1 row) + +RESET ROLE; +DROP FUNCTION format(text, regclass, text); +DROP EXTENSION IF EXISTS aqo; +-- Test 2 +RESET ROLE; +ALTER ROLE regress_hacker NOSUPERUSER; +SET ROLE regress_hacker; +SHOW is_superuser; + is_superuser +-------------- + off +(1 row) + +CREATE FUNCTION aqo_migrate_to_1_1_get_pk(rel text) +RETURNS regclass +AS $$ +DECLARE + ret regclass; +BEGIN + ALTER ROLE regress_hacker SUPERUSER; + SELECT * FROM aqo_migrate_to_1_1_get_pk(rel::regclass) INTO ret; + RETURN ret; +END +$$ LANGUAGE plpgsql; +RESET ROLE; +CREATE EXTENSION aqo; +-- Test result (must be 'off') +SET ROLE regress_hacker; +SHOW is_superuser; + is_superuser +-------------- + off +(1 row) + +RESET ROLE; +DROP FUNCTION aqo_migrate_to_1_1_get_pk(text); +DROP EXTENSION IF EXISTS aqo; +-- Cleanup +RESET ROLE; +DROP ROLE regress_hacker; diff --git a/sql/aqo_CVE-2020-14350.sql b/sql/aqo_CVE-2020-14350.sql new file mode 100644 index 00000000..51521dca --- /dev/null +++ b/sql/aqo_CVE-2020-14350.sql @@ -0,0 +1,71 @@ +/* + * Check fix for CVE-2020-14350. + * See also 7eeb1d986 postgresql commit. + */ + +SET client_min_messages = 'warning'; +DROP ROLE IF EXISTS regress_hacker; +SET client_min_messages = 'notice'; + +CREATE ROLE regress_hacker LOGIN; + +-- Test 1 +RESET ROLE; +ALTER ROLE regress_hacker NOSUPERUSER; + +SET ROLE regress_hacker; +SHOW is_superuser; +CREATE FUNCTION format(f text, r regclass, t text) +RETURNS text +AS $$ +BEGIN + ALTER ROLE regress_hacker SUPERUSER; + RETURN ''; +END +$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; + +RESET ROLE; +CREATE EXTENSION aqo; + +-- Test result (must be 'off') +SET ROLE regress_hacker; +SHOW is_superuser; + +RESET ROLE; +DROP FUNCTION format(text, regclass, text); +DROP EXTENSION IF EXISTS aqo; + +-- Test 2 +RESET ROLE; +ALTER ROLE regress_hacker NOSUPERUSER; + +SET ROLE regress_hacker; +SHOW is_superuser; + +CREATE FUNCTION aqo_migrate_to_1_1_get_pk(rel text) +RETURNS regclass +AS $$ +DECLARE + ret regclass; +BEGIN + ALTER ROLE regress_hacker SUPERUSER; + SELECT * FROM aqo_migrate_to_1_1_get_pk(rel::regclass) INTO ret; + RETURN ret; +END +$$ LANGUAGE plpgsql; + +RESET ROLE; +CREATE EXTENSION aqo; + +-- Test result (must be 'off') +SET ROLE regress_hacker; +SHOW is_superuser; + +RESET ROLE; +DROP FUNCTION aqo_migrate_to_1_1_get_pk(text); +DROP EXTENSION IF EXISTS aqo; + +-- Cleanup +RESET ROLE; +DROP ROLE regress_hacker; + diff --git a/t/000_security.pl b/t/000_security.pl deleted file mode 100644 index 7caf1ba3..00000000 --- a/t/000_security.pl +++ /dev/null @@ -1,43 +0,0 @@ -# Acquiring superuser privileges -use strict; -use warnings; -use PostgresNode; -use TestLib; -use Test::More tests => 1; - -my $node; - -# Initialize node -$node = get_new_node('node'); -$node->init; -$node->start; - -my $query; -my $is_su; - -print($node->safe_psql("postgres", "CREATE USER regress_hacker LOGIN")); -$is_su = $node->safe_psql('postgres', undef, - extra_params => [ '-U', 'regress_hacker', '-c', 'SHOW is_superuser' ]); -diag("The regress_hacker is superuser: " . $is_su . "\n"); - -$query = q{ -CREATE FUNCTION format(f text, r regclass, t text) -RETURNS text -AS $$ -BEGIN - ALTER ROLE regress_hacker SUPERUSER; - RETURN ''; -END -$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT; -}; - -print($node->safe_psql('postgres', undef, - extra_params => [ '-U', 'regress_hacker', '-c', $query ]) . "\n"); - -$node->psql("postgres", "CREATE EXTENSION aqo"); - -$is_su = $node->safe_psql('postgres', undef, - extra_params => [ '-U', 'regress_hacker', '-c', 'SHOW is_superuser' ]); - -diag("The regress_hacker is superuser: " . $is_su . "\n"); -ok($is_su eq 'off');