Skip to content

Commit 3efa508

Browse files
craig[bot]kyle-a-wong
andcommitted
Merge #155530
155530: logictest: flush out indirect unsafe access in logictests r=angles-n-daemons a=angles-n-daemons We recently added a session variable which must be explicitly set for a caller to access system and crdb_internal tables. There's a concern however that the system will block due to "indirect" access, be it query expansion, rewriting or anything else. So users aren't surprised by this behavior, this PR modifies the logictests so that by default, unless a query explicitly references the system or crdb_internal tables, it will disallow access to the unsafe internals. Doing so will hopefully flush out any possible instances of unsafe access. This PR also does a few things to make this possible: - Introduces a way in tests to override the session variable. - Adds a new knob option for the logictests, so that if there's indirect access because a UDF is defined, the test writer can explicitly allow for it. Fixes: #154675 Fixes: #155367 Epic: [CRDB-55276](https://cockroachlabs.atlassian.net/browse/CRDB-55276) Release note: None Co-authored-by: Kyle Wong <[email protected]>
2 parents b4ddc73 + 265d86f commit 3efa508

31 files changed

+188
-30
lines changed

pkg/ccl/logictestccl/testdata/logic_test/multi_region_survival_goal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# knob-opt: sync-event-log
1+
# knob-opt: sync-event-log allow-unsafe
22
# LogicTest: multiregion-9node-3region-3azs-tenant
33

44
# Only the root user can modify the system database's regions.

pkg/ccl/logictestccl/testdata/logic_test/plpgsql_txn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# LogicTest: !local-prepared
2+
# knob-opt: allow-unsafe
23

34
statement ok
45
CREATE TABLE t (x INT);

pkg/ccl/logictestccl/testdata/logic_test/redact_descriptor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# LogicTest: !schema-locked-disabled
2+
# knob-opt: allow-unsafe
23

34
# NB: "Xw==" is the byte representation of "_", which is what we use to redact fields.
45
statement ok

pkg/ccl/logictestccl/testdata/logic_test/triggers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# LogicTest: !local-legacy-schema-changer !local-prepared
2+
# knob-opt: allow-unsafe
23

34
# Include some hidden columns that won't be visible to triggers.
45
statement ok

pkg/ccl/logictestccl/testdata/logic_test/udf_rewrite

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# knob-opt: allow-unsafe
2+
13
statement ok
24
CREATE SEQUENCE seq;
35

pkg/sql/authorization.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ func (p *planner) HasPrivilege(
125125
// Do a safety check on the object, if it is considered unsafe
126126
// does the caller have the appropriate session data to access it?
127127
if p.objectIsUnsafe(ctx, privilegeObject) {
128-
if err := unsafesql.CheckInternalsAccess(ctx, p.SessionData(), p.stmt.AST, p.extendedEvalCtx.Annotations, &p.ExecCfg().Settings.SV); err != nil {
128+
unsafeOverride := p.ExecCfg().EvalContextTestingKnobs.UnsafeOverride
129+
if err := unsafesql.CheckInternalsAccess(ctx, p.SessionData(), p.stmt.AST, p.extendedEvalCtx.Annotations, &p.ExecCfg().Settings.SV, unsafeOverride); err != nil {
129130
return false, err
130131
}
131132
}
@@ -961,7 +962,7 @@ func (p *planner) objectIsUnsafe(ctx context.Context, privilegeObject privilege.
961962
return false
962963
}
963964

964-
// All system descriptors are considered unsafe.
965+
// All system descriptors are considered unsafe
965966
if catalog.IsSystemDescriptor(d) {
966967
return true
967968
}

pkg/sql/crdb_internal.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4491,6 +4491,13 @@ CREATE TABLE crdb_internal.ranges_no_leases (
44914491
`,
44924492
resultColumns: colinfo.RangesNoLeases,
44934493
generator: func(ctx context.Context, p *planner, _ catalog.DatabaseDescriptor, limit int64, _ *stop.Stopper) (virtualTableGenerator, cleanupFunc, error) {
4494+
// We have to disable the unsafe check on execution, because for this table
4495+
// we may have gotten to it through delegate execution, eg. SHOW RANGES.
4496+
// Accessing it will still fail if queried directly during planning, eg
4497+
// `SELECT * FROM crdb_internal.ranges_no_leases`, so it's safe to unblock
4498+
// here.
4499+
defer p.DisableUnsafeInternalsCheck()()
4500+
44944501
hasAdmin, err := p.HasAdminRole(ctx)
44954502
if err != nil {
44964503
return nil, nil, err

pkg/sql/logictest/logic.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ import (
330330
// - noticetrace: runs the query and compares only the notices that
331331
// appear. Cannot be combined with kvtrace.
332332
// - nodeidx=N: runs the query on node N of the cluster.
333+
// - allowunsafe: allows access to unsafe internals during execution.
333334
//
334335
// The label is optional. If specified, the test runner stores a hash
335336
// of the results of the query under the given label. If the label is
@@ -969,6 +970,9 @@ type logicQuery struct {
969970
// roundFloatsInStringsSigFigs specifies the number of significant figures
970971
// to round floats embedded in strings to where zero means do not round.
971972
roundFloatsInStringsSigFigs int
973+
974+
// allowUnsafe indicates whether unsafe operations are allowed during execution.
975+
allowUnsafe bool
972976
}
973977

974978
var allowedKVOpTypes = []string{
@@ -1119,6 +1123,10 @@ type logicTest struct {
11191123
// retryDuration is the maximum duration to retry a statement when using
11201124
// the retry directive.
11211125
retryDuration time.Duration
1126+
1127+
// allowUnsafe is a variable which controls whether the test can access
1128+
// unsafe internals.
1129+
allowUnsafe bool
11221130
}
11231131

11241132
func (t *logicTest) t() *testing.T {
@@ -1493,6 +1501,7 @@ func (t *logicTest) newCluster(
14931501
DisableOptimizerRuleProbability: *disableOptRuleProbability,
14941502
OptimizerCostPerturbation: *optimizerCostPerturbation,
14951503
ForceProductionValues: serverArgs.ForceProductionValues,
1504+
UnsafeOverride: func() *bool { return &t.allowUnsafe },
14961505
}
14971506
knobs.SQLExecutor = &sql.ExecutorTestingKnobs{
14981507
DeterministicExplain: true,
@@ -2127,6 +2136,18 @@ func (c knobOptSynchronousEventLog) apply(args *base.TestingKnobs) {
21272136
args.EventLog.(*eventlog.EventLogTestingKnobs).SyncWrites = true
21282137
}
21292138

2139+
// knobOptAllowUnsafe always allows access to the unsafe internals.
2140+
type knobOptAllowUnsafe struct{}
2141+
2142+
var _ knobOpt = knobOptAllowUnsafe{}
2143+
2144+
// apply implements the clusterOpt interface.
2145+
func (c knobOptAllowUnsafe) apply(args *base.TestingKnobs) {
2146+
e := args.SQLEvalContext.(*eval.TestingKnobs)
2147+
v := true
2148+
e.UnsafeOverride = func() *bool { return &v }
2149+
}
2150+
21302151
// clusterOptIgnoreStrictGCForTenants corresponds to the
21312152
// ignore-tenant-strict-gc-enforcement directive.
21322153
type clusterOptIgnoreStrictGCForTenants struct{}
@@ -2280,6 +2301,8 @@ func readKnobOptions(t *testing.T, path string) []knobOpt {
22802301
res = append(res, knobOptDisableCorpusGeneration{})
22812302
case "sync-event-log":
22822303
res = append(res, knobOptSynchronousEventLog{})
2304+
case "allow-unsafe":
2305+
res = append(res, knobOptAllowUnsafe{})
22832306
default:
22842307
t.Fatalf("unrecognized knob option: %s", opt)
22852308
}
@@ -2966,6 +2989,9 @@ func (t *logicTest) processSubtest(
29662989
case "async":
29672990
query.expectAsync = true
29682991

2992+
case "allowunsafe":
2993+
query.allowUnsafe = true
2994+
29692995
default:
29702996
if strings.HasPrefix(opt, "round-in-strings") {
29712997
significantFigures, err := floatcmp.ParseRoundInStringsDirective(opt)
@@ -3611,6 +3637,7 @@ func (t *logicTest) unexpectedError(sql string, pos string, err error) (bool, er
36113637
var uniqueHashPattern = regexp.MustCompile(`UNIQUE.*USING\s+HASH`)
36123638

36133639
func (t *logicTest) execStatement(stmt logicStatement, disableCFMutator bool) (bool, error) {
3640+
defer t.setSafetyGate(stmt.sql, false)()
36143641
db := t.db
36153642
t.noticeBuffer = nil
36163643
if *showSQL {
@@ -3734,6 +3761,7 @@ func (t *logicTest) hashResults(results []string) (string, error) {
37343761
}
37353762

37363763
func (t *logicTest) execQuery(query logicQuery) error {
3764+
defer t.setSafetyGate(query.sql, query.allowUnsafe)()
37373765
if *showSQL {
37383766
t.outf("%s;", query.sql)
37393767
}
@@ -4621,6 +4649,7 @@ func RunLogicTest(
46214649
perErrorSummary: make(map[string][]string),
46224650
rng: rng,
46234651
declarativeCorpusCollector: cc,
4652+
allowUnsafe: true,
46244653
}
46254654
if *printErrorSummary {
46264655
defer lt.printErrorSummary()
@@ -4896,3 +4925,23 @@ func locateCockroachPredecessor(version string) (string, error) {
48964925
}
48974926
return path, nil
48984927
}
4928+
4929+
// setSafetyGate is a utility function which controls whether access to the unsafe
4930+
// internals is allowed by the contained sql statement. The reasoning behind it is
4931+
// as follows. We want queries which explicitly access unsafe internals to have
4932+
// access to them, but we want to prevent indirect access wherever possible.
4933+
// Indirect access can be described as any query which doesn't reference an unsafe
4934+
// object, but still accesses it under the hood. We want to flush out these cases
4935+
// so that users can never execute statements which look safe, but block when executed.
4936+
func (t *logicTest) setSafetyGate(sql string, skip bool) func() {
4937+
sql = strings.ToLower(sql)
4938+
explicitlyUnsafe := strings.Contains(sql, "crdb_internal.") || strings.Contains(sql, "system.")
4939+
if skip || explicitlyUnsafe {
4940+
return func() {}
4941+
}
4942+
4943+
t.allowUnsafe = false
4944+
return func() {
4945+
t.allowUnsafe = true
4946+
}
4947+
}

pkg/sql/logictest/parallel_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,14 @@ func (t *parallelTest) processTestFile(path string, nodeIdx int, db *gosql.DB, c
6767
// Set up a dummy logicTest structure to use that code.
6868
rng, _ := randutil.NewTestRand()
6969
l := &logicTest{
70-
rootT: t.T,
71-
cluster: t.cluster,
72-
nodeIdx: nodeIdx,
73-
db: db,
74-
user: username.RootUser,
75-
verbose: testing.Verbose() || log.V(1),
76-
rng: rng,
70+
rootT: t.T,
71+
cluster: t.cluster,
72+
nodeIdx: nodeIdx,
73+
db: db,
74+
user: username.RootUser,
75+
verbose: testing.Verbose() || log.V(1),
76+
rng: rng,
77+
allowUnsafe: true,
7778
}
7879
if err := l.processTestFile(path, logictest.TestClusterConfig{}); err != nil {
7980
log.Dev.Errorf(context.Background(), "error processing %s: %s", path, err)

pkg/sql/logictest/testdata/logic_test/bytes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ PREPARE r1(bytes) AS
156156
SELECT id FROM system.namespace WHERE name = 'defaultdb'
157157
);
158158

159-
query T
159+
query T allowunsafe
160160
EXECUTE r1('abc')
161161
----
162162
\022[\012\011defaultdb\020d\0320\012\013\012\005admin\020\002\030\002\012\015\012\006public\020\200\020\030\000\012\012\012\004root\020\002\030\002\022\004root\030\003"\000(\001:\014\012\006public\022\002\010e@\000J\000Z\002\020\000p\000

0 commit comments

Comments
 (0)