Skip to content

Commit f36bd8d

Browse files
joemcmahontonycoz
authored andcommitted
[FIX] Make debugger l range specs sane
Fixes 21350. The pattern which matches valid line number ranges in the debugger is far too complaisant when it comes to strings it will accept. A number of completely nonsensical values are cheerfully accepted and produce results that range from nothing at all to very odd results indeed: - specifying a float for a line number appends the fractional part of the float to ALL line numbers until something else sets the starting line number to an integer again. - Strings like `.$.$.$`, `.....`, and `22.$` are all acccepted. Some do nothing, some generate errors. - Negative line numbers are accepted but do nothing. This tracks back to a catchall line-range pattern that was added in the Perl *3* debugger at commit a687059. This pattern looks as if it were added to eventually implement a number of things, including lines relative to the current line, negative indexes into the magic source code array, variable references, and so on. Several of these were implemented elsewhere in the debugger, but the regex was never simplified. This change breaks up that regex and cleans up a few others (including code that internally generates possible negative line numbers). Changes to the old catchall regex: - Remove the conditional match of a leading `-`. Negative line numbers are not permitted, at all. The code that generated them internally has been fixed so it does not do so. - Remove `$` as a valid character in a linespec in the catchall regex. `$scalar` is handled explicitly in the _cmd_l_main code. There is no other documented use for bare '$' in a list linespec, even though it is accepted. - The linespec character class treated `.`, and digits, as equally valid characters in a linespec. This led to the linespecs matching floating point numbers, IPv4 addresses, and various nonnumeric nonsense that did not translate to a valid line number. The combined character match was split into either a single period (handled already as "the current line") or a series of digits _only_ (a possible line number). This had to be done for both the starting and ending linespec. - \A and \z were added to the catchall regex to ensure that it matched the whole of the remaining line, or failed. This prevents things like `...` from being matched as equivalent to `.` alone. - Make sure the 'v' command creates good linespecs internally. As previously coded, this could generate linespecs that started with a negative number. Since we've outlawed negative numbers in linespecs, a `v` too close to the start of the file (as occurs in the tests for perl5db.pl) would cause the more-stringent range check to throw an error. Instead, the 'v' command now checks the generated step back and sets it to 1 if it is less than 1. The existing test is sufficient to confirm that the new code in cmd_v fixes the issue. - Verify that floating-point line numbers are illegal: This was the original bug that triggered this change: the original regex accepted floating-point numbers, and because indexing an array in Perl with a float works due toimplicit type conversions, and the increment of a float by (integer) 1 doesn't downgrade the float to an int, the line numbers continued to be displayed with the fractional portion of the original number until something intervened with an explicit or implicit integer linespec. We simply don't permit them to be specified as line numbers now. - Use \w for valid variable name characters: The debugger should eventually be UTF-8 compatible, but is not; this is a step along the way to get there. It makes the variable match in the cmd_l reference parsing code match proper Perl variable names, including UTF-8 "word" characters, and now also properly matches regex result variables ($1, $2, etc.). It explicitly rules out bare $, which the old regex would match, triggering an error. (For now, we've added the `/a` flag to the match to ensure only ASCII matches, since the rest of the code isn't prepared to handle non-ASCII numerics.) - A new perldb/t test file was added to verify that all the range cleanups work properly, along with new stanzas in the perl5db.pl test suite. It simply lets us start up the debugger and have a place to run the bad linespecs from to verify they do _not_ work. Changes from @mauke's code review - Actually add the new test file to MANIFEST, d'oh. - Vastly simpler `_cmd_l_calc_initial_end_and_i` logic. Also more likely to actually be correct. - More precise variable name matching for `l $scalar`. - Purposefully limit line spec numbers to ASCII. - Update debugger version to pass porting/cmp_version.t. Clean up calling sequences The calling sequences were not always consistent, and some of the changes to clean up the code ment that variables formerly passed in now no longer needed to be. One sub was using a variable in the caller; this was fixed to be passed in explicitly. - _cmd_l_handle_var_name now gets $subname passed in - _cmd_l_calc_initial_end_and_i no longer needs spec - _cmd_l_calc_initial_end_and_i chained teriary replaced with chanined defined-or
1 parent 65038fe commit f36bd8d

File tree

4 files changed

+201
-32
lines changed

4 files changed

+201
-32
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5430,6 +5430,7 @@ lib/perl5db/t/filename-line-breakpoint Tests for the Perl debugger
54305430
lib/perl5db/t/gh-17660 Tests for the Perl debugger
54315431
lib/perl5db/t/gh-17661 Tests for the Perl debugger
54325432
lib/perl5db/t/gh-17661b Tests for the Perl debugger
5433+
lib/perl5db/t/gh-21350 Tests for the Perl debugger
54335434
lib/perl5db/t/load-modules Tests for the Perl debugger
54345435
lib/perl5db/t/lsub-n Test script used by perl5db.t
54355436
lib/perl5db/t/lvalue-bug Tests for the Perl debugger

lib/perl5db.pl

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ BEGIN
532532
use vars qw($VERSION $header);
533533

534534
# bump to X.XX in blead, only use X.XX_XX in maint
535-
$VERSION = '1.77';
535+
$VERSION = '1.78';
536536

537537
$header = "perl5db.pl version $VERSION";
538538

@@ -2609,7 +2609,7 @@ sub _cmd_l_handle_var_name {
26092609

26102610
sub _cmd_l_handle_subname {
26112611

2612-
my $s = $subname;
2612+
my $s = my $subname = shift;
26132613

26142614
# De-Perl4.
26152615
$subname =~ s/\'/::/;
@@ -2620,9 +2620,9 @@ sub _cmd_l_handle_subname {
26202620
# Put it in CORE::GLOBAL if t doesn't start with :: and
26212621
# it doesn't live in this package and it lives in CORE::GLOBAL.
26222622
$subname = "CORE::GLOBAL::$s"
2623-
if not defined &$subname
2624-
and $s !~ /::/
2625-
and defined &{"CORE::GLOBAL::$s"};
2623+
if not defined &$subname
2624+
and $s !~ /::/
2625+
and defined &{"CORE::GLOBAL::$s"};
26262626

26272627
# Put leading '::' names into 'main::'.
26282628
$subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
@@ -2691,34 +2691,25 @@ sub _cmd_l_plus {
26912691
}
26922692

26932693
sub _cmd_l_calc_initial_end_and_i {
2694-
my ($spec, $start_match, $end_match) = @_;
2694+
my ($current_line, $start_match, $end_match) = @_;
26952695

2696-
# Determine end point; use end of file if not specified.
2697-
my $end = ( !defined $start_match ) ? $max :
2698-
( $end_match ? $end_match : $start_match );
2699-
2700-
# Go on to the end, and then stop.
2696+
my $end = $end_match // $start_match // $max;
2697+
# Clean up the end spec if needed.
2698+
$end = $current_line if $end eq '.';
27012699
_minify_to_max(\$end);
27022700

2703-
# Determine start line.
2704-
my $i = $start_match;
2705-
2706-
if ($i eq '.') {
2707-
$i = $spec;
2708-
}
2709-
2710-
$i = _max($i, 1);
2711-
2712-
$incr = $end - $i;
2701+
# Determine the loop start point.
2702+
my $i = $start_match // 1;
2703+
$i = $current_line if $i eq '.';
27132704

27142705
return ($end, $i);
27152706
}
27162707

27172708
sub _cmd_l_range {
2718-
my ($spec, $current_line, $start_match, $end_match) = @_;
2709+
my ($current_line, $start_match, $end_match) = @_;
27192710

27202711
my ($end, $i) =
2721-
_cmd_l_calc_initial_end_and_i($spec, $start_match, $end_match);
2712+
_cmd_l_calc_initial_end_and_i($current_line, $start_match, $end_match);
27222713

27232714
# If we're running under a client editor, force it to show the lines.
27242715
if ($client_editor) {
@@ -2780,18 +2771,15 @@ sub _cmd_l_range {
27802771
sub _cmd_l_main {
27812772
my $spec = shift;
27822773

2783-
# If this is '-something', delete any spaces after the dash.
2784-
$spec =~ s/\A-\s*\z/-/;
2785-
27862774
# If the line is '$something', assume this is a scalar containing a
27872775
# line number.
27882776
# Set up for DB::eval() - evaluate in *user* context.
2789-
if ( my ($var_name) = $spec =~ /\A(\$.*)/s ) {
2790-
return _cmd_l_handle_var_name($var_name);
2777+
if ( $spec =~ /\A(\$(?:[0-9]+|[^\W\d]\w*))\z/ ) {
2778+
return _cmd_l_handle_var_name($spec);
27912779
}
27922780
# l name. Try to find a sub by that name.
27932781
elsif ( ($subname) = $spec =~ /\A([\':A-Za-z_][\':\w]*(?:\[.*\])?)/s ) {
2794-
return _cmd_l_handle_subname();
2782+
return _cmd_l_handle_subname($subname);
27952783
}
27962784
# Bare 'l' command.
27972785
elsif ( $spec !~ /\S/ ) {
@@ -2802,8 +2790,13 @@ sub _cmd_l_main {
28022790
return _cmd_l_plus($new_start, $new_incr);
28032791
}
28042792
# l start-stop or l start,stop
2805-
elsif (my ($s, $e) = $spec =~ /^(?:(-?[\d\$\.]+)(?:[-,]([\d\$\.]+))?)?/ ) {
2806-
return _cmd_l_range($spec, $line, $s, $e);
2793+
# Purposefully limited to ASCII; UTF-8 support would be nice sometime.
2794+
elsif (my ($s, $e) = $spec =~ /\A(?:(\.|\d+)(?:[-,](\.|\d+))?)?\z/a ) {
2795+
return _cmd_l_range($line, $s, $e);
2796+
}
2797+
# Protest at bizarre and incorrect specs.
2798+
else {
2799+
print {$OUT} "Invalid line specification '$spec'.\n";
28072800
}
28082801

28092802
return;
@@ -6033,8 +6026,9 @@ sub cmd_v {
60336026
# Set the start to the argument given (if there was one).
60346027
$start = $1 if $1;
60356028

6036-
# Back up by the context amount.
6029+
# Back up by the context amount. Don't back up past line 1.
60376030
$start -= $preview;
6031+
$start = 1 unless $start > 0;
60386032

60396033
# Put together a linespec that _cmd_l_main will like.
60406034
$line = $start . '-' . ( $start + $incr );

lib/perl5db.t

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3296,6 +3296,177 @@ EOS
32963296
"[github #19198] check we stopped correctly");
32973297
}
32983298

3299+
{
3300+
# gh-21350: verify that nonsense linespecs are rejected #1
3301+
my $wrapper = DebugWrap->new(
3302+
{
3303+
cmds =>
3304+
[
3305+
'l ...',
3306+
'q',
3307+
],
3308+
prog => '../lib/perl5db/t/gh-21350',
3309+
}
3310+
);
3311+
3312+
$wrapper->contents_like(
3313+
qr/Invalid line specification '...'/,
3314+
q/gh-21350: multiple periods rejected/,
3315+
);
3316+
}
3317+
3318+
{
3319+
# gh-21350: verify that nonsense linespecs are rejected #2
3320+
my $wrapper = DebugWrap->new(
3321+
{
3322+
cmds =>
3323+
[
3324+
'l $',
3325+
'q',
3326+
],
3327+
prog => '../lib/perl5db/t/gh-21350',
3328+
}
3329+
);
3330+
3331+
$wrapper->contents_like(
3332+
qr/Invalid line specification '\$'/,
3333+
q/gh-21350: $ rejected/,
3334+
);
3335+
}
3336+
3337+
{
3338+
# gh-21350: verify that nonsense linespecs are rejected #3
3339+
my $wrapper = DebugWrap->new(
3340+
{
3341+
cmds =>
3342+
[
3343+
'l 2.71828',
3344+
'q',
3345+
],
3346+
prog => '../lib/perl5db/t/gh-21350',
3347+
}
3348+
);
3349+
3350+
$wrapper->contents_like(
3351+
qr/Invalid line specification '2\.71828'/,
3352+
q/gh-21350: floating-point rejected/,
3353+
);
3354+
}
3355+
3356+
{
3357+
# gh-21350: verify that nonsense linespecs are rejected #4
3358+
my $wrapper = DebugWrap->new(
3359+
{
3360+
cmds =>
3361+
[
3362+
'l 1.1.1.1',
3363+
'q',
3364+
],
3365+
prog => '../lib/perl5db/t/gh-21350',
3366+
}
3367+
);
3368+
3369+
$wrapper->contents_like(
3370+
qr/Invalid line specification '1\.1\.1\.1'/,
3371+
q/gh-21350: IPv4 address rejected/,
3372+
);
3373+
}
3374+
3375+
{
3376+
# gh-21350: verify that nonsense linespecs are rejected #5
3377+
my $wrapper = DebugWrap->new(
3378+
{
3379+
cmds =>
3380+
[
3381+
'l -.',
3382+
'q',
3383+
],
3384+
prog => '../lib/perl5db/t/gh-21350',
3385+
}
3386+
);
3387+
3388+
$wrapper->contents_like(
3389+
qr/Invalid line specification '-\.'/,
3390+
q/gh-21350: invalid partial range rejected/,
3391+
);
3392+
}
3393+
3394+
{
3395+
# gh-21350: verify that nonsense linespecs are rejected #6
3396+
my $wrapper = DebugWrap->new(
3397+
{
3398+
cmds =>
3399+
[
3400+
'l -$.',
3401+
'q',
3402+
],
3403+
prog => '../lib/perl5db/t/gh-21350',
3404+
}
3405+
);
3406+
3407+
$wrapper->contents_like(
3408+
qr/Invalid line specification '\-\$\.'/,
3409+
q/gh-21350: formerly acceptable nonsense rejected/,
3410+
);
3411+
}
3412+
3413+
{
3414+
# gh-21350: verify that nonsense linespecs are rejected #7
3415+
my $wrapper = DebugWrap->new(
3416+
{
3417+
cmds =>
3418+
[
3419+
'l -12',
3420+
'q',
3421+
],
3422+
prog => '../lib/perl5db/t/gh-21350',
3423+
}
3424+
);
3425+
3426+
$wrapper->contents_like(
3427+
qr/Invalid line specification '-12'/,
3428+
q/gh-21350: negative line number rejected/,
3429+
);
3430+
}
3431+
3432+
{
3433+
# gh-21350: verify that nonsense linespecs are rejected #8
3434+
my $wrapper = DebugWrap->new(
3435+
{
3436+
cmds =>
3437+
[
3438+
'l 17$',
3439+
'q',
3440+
],
3441+
prog => '../lib/perl5db/t/gh-21350',
3442+
}
3443+
);
3444+
3445+
$wrapper->contents_like(
3446+
qr/Invalid line specification '17\$'/,
3447+
q/gh-21350: line number with trailing $ rejected/,
3448+
);
3449+
}
3450+
3451+
{
3452+
# gh-21350: verify that nonsense linespecs are rejected #9
3453+
my $wrapper = DebugWrap->new(
3454+
{
3455+
cmds =>
3456+
[
3457+
'l $2250$',
3458+
'q',
3459+
],
3460+
prog => '../lib/perl5db/t/gh-21350',
3461+
}
3462+
);
3463+
3464+
$wrapper->contents_like(
3465+
qr/Invalid line specification '\$2250\$'/,
3466+
q/gh-21350: match variable with trailing $ rejected/,
3467+
);
3468+
}
3469+
32993470
done_testing();
33003471

33013472
END {

lib/perl5db/t/gh-21350

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!perl
2+
3+
print "minimal program to allow the debugger to launch\n";

0 commit comments

Comments
 (0)