Dancer2 icon indicating copy to clipboard operation
Dancer2 copied to clipboard

Route exception: Not a CODE reference triggers an infinite consuming memory loop which CRASH system

Open markoong opened this issue 7 years ago • 3 comments

I was following along a version of the tutorial which didn't had this change fa582edca53dcc5cdf47df5966fc97ac5bdd59da and on correct user POST login the dancr.pl script enter and infinite loop which literally crash the system consuming GBs of RAM in a matter of seconds.

The output is:

Dancer2 v0.205000 server 7461 listening on http://0.0.0.0:3000

[main:7461] error @2017-09-10 17:59:24> Route exception: Not a CODE reference at dancr.pl line 97. in /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/App.pm l. 1471 Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 54. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 54. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 108. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 108. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 162. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 162. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 216. Use of uninitialized value in subroutine entry at /home/user/.perlbrew/libs/perl-5.22.1@dancer2/lib/perl5/Dancer2/Core/Error.pm line 338, <$fh> line 216.

The full script is:

#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';

use Dancer2;
use DBI;
use File::Spec;
use File::Slurper qw/ read_text /;
use Template;

set 'database'     => File::Spec->catfile(File::Spec->tmpdir(), 'dancr.db');
set 'session'      => 'Simple';
set 'template'     => 'template_toolkit';
set 'logger'       => 'console';
set 'log'          => 'debug';
set 'show_errors'  => 1;
set 'startup_info' => 1;
set 'warnings'     => 1;
set 'username'     => 'admin';
set 'password'     => 'password';
set 'layout'       => 'tutorial';

my $flash;

sub set_flash {
    my $message = shift;

    $flash = $message;
}

sub get_flash {

    my $msg = $flash;
    $flash = "";

    return $msg;
}

sub connect_db {
    my $dbh = DBI->connect("dbi:SQLite:dbname=".setting('database')) or
        die $DBI::errstr;

    return $dbh;
}

sub init_db {
    my $db = connect_db();
    my $schema = read_text('./schema.sql');
    $db->do($schema) or die $db->errstr;
}

hook before_template_render => sub {
        my $tokens = shift;

        $tokens->{'css_url'} = request->base . 'css/style.css';
        $tokens->{'login_url'} = uri_for('/login');
        $tokens->{'logout_url'} = uri_for('/logout');
    };

get '/' => sub {
        my $db = connect_db();
        my $sql = 'select id, title, text from entries order by id desc';
        my $sth = $db->prepare($sql) or die $db->errstr;
        $sth->execute or die $sth->errstr;
        template 'show_entries.tt', {
                'msg' => get_flash(),
                'add_entry_url' => uri_for('/add'),
                'entries' => $sth->fetchall_hashref('id'),
            };
    };

post '/add' => sub {
        if ( not session('logged_in') ) {
            send_error("Not logged in", 401);
        }

        my $db = connect_db();
        my $sql = 'insert into entries (title, text) values (?, ?)';
        my $sth = $db->prepare($sql) or die $db->errstr;
        $sth->execute(
            body_parameters->get('title'),
            body_parameters->get('text')
        ) or die $sth->errstr;

        set_flash('New entry posted!');
        redirect '/';
    };

any ['get', 'post'] => '/login' => sub {
        my $err;

        if ( request->method() eq "POST" ) {
            # process form input
            if ( body_parameters->get('username') ne setting('username') ) {
                $err = "Invalid username";
            }
            elsif ( body_parameters->('password') ne setting('password') ) {
                $err = "Invalid password";
            }
            else {
                session 'logged_in' => true;
                set_flash('You are logged in.');
                return redirect '/';
            }
        }

        # display login form
        template 'login.tt', {
                'err' => $err,
            };

    };

get '/logout' => sub {
        app->destroy_session;
        set_flash('You are logged out.');
        redirect '/';
    };

init_db();
start;

markoong avatar Sep 10 '17 21:09 markoong

This behavior still happens on v0.205002; Although it is triggered by a bug in the user's code, this should be handled gracefully rather than by exhausting system resources. With this issue, any trivial bug in someone's code can be elevated to a denial of service.

17dec avatar Dec 04 '17 18:12 17dec

Here's a trimmed down example that triggers the same behavior:

#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
use Dancer2;

set 'template'     => 'simple';
set 'show_errors'  => 1;

get '/' => sub { template 'this_template_doesnt_exist.tt'; };

start;

It seems that 'show_errors' combined with a nonexisting template is the cause. The good part is that, hopefully, nobody has 'show_errors' enabled in production, so the impact of this bug should be limited to development setups.

17dec avatar Dec 05 '17 05:12 17dec

I believe the tutorial includes the missing template, but show_errors plus a missing template shouldn't do that. Considering this a bug until proved otherwise.

cromedome avatar Aug 11 '23 01:08 cromedome