Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

malicious screen output can make mosh-server assertion fail at line 122 #667

kcwu opened this issue Aug 21, 2015 · 10 comments


Copy link

@kcwu kcwu commented Aug 21, 2015

How to reproduce:
Inside mosh, run following code

echo -e '0\e[1J\xcc\xb4'

then mosh-server will crash

Assertion failed: (this_cell == combining_cell), function print, file, line 122.

from gdb, the call stack

#0  0x00000008020c83ca in thr_kill () from /lib/
#1  0x00000008020c8378 in raise () from /lib/
#2  0x00000008020c6b99 in abort () from /lib/
#3  0x00000008020a6fe1 in __assert () from /lib/
#4  0x000000000041e43e in Terminal::Emulator::print (this=0x7fffffffe4c0, act=0x802c1b140) at
#5  0x000000000044a9dd in Parser::Print::act_on_terminal (this=0x802c1b140, emu=0x7fffffffe4c0) at
#6  0x000000000044cd55 in Terminal::Complete::act (this=0x7fffffffe4a8, str=...) at

this issue is found by afl-fuzz

Copy link

@eminence eminence commented Aug 21, 2015

I've not been able to reproduce this crash on mosh 1.2.4, or 1.2.5.

What version of mosh are you running (both client and server)?

Copy link

@andersk andersk commented Aug 21, 2015

Confirmed with 1.2.5.

termemu: void Terminal::Emulator::print(const Parser::Print*): Assertion `this_cell == combining_cell' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff6b45267 in __GI_raise (sig=sig@entry=6)
    at ../sysdeps/unix/sysv/linux/raise.c:55
55  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff6b45267 in __GI_raise (sig=sig@entry=6)
    at ../sysdeps/unix/sysv/linux/raise.c:55
#1  0x00007ffff6b46eca in __GI_abort () at abort.c:89
#2  0x00007ffff6b3e03d in __assert_fail_base (
    fmt=0x7ffff6ca0028 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=assertion@entry=0x555555589955 "this_cell == combining_cell", 
    file=file@entry=0x55555558a743 "", line=line@entry=122, 
    function=function@entry=0x5555555899a0 <Terminal::Emulator::print(Parser::Print const*)::__PRETTY_FUNCTION__> "void Terminal::Emulator::print(const Parser::Print*)") at assert.c:92
#3  0x00007ffff6b3e0f2 in __GI___assert_fail (
    assertion=assertion@entry=0x555555589955 "this_cell == combining_cell", 
    file=file@entry=0x55555558a743 "", line=line@entry=122, 
    function=function@entry=0x5555555899a0 <Terminal::Emulator::print(Parser::Print const*)::__PRETTY_FUNCTION__> "void Terminal::Emulator::print(const Parser::Print*)") at assert.c:101
#4  0x000055555556d658 in Terminal::Emulator::print (this=0x7fffffff9568, 
    act=0x5555557a8a20) at
#5  0x000055555556858c in Parser::Print::act_on_terminal (
    this=<optimized out>, emu=<optimized out>) at
#6  0x00005555555837d3 in Terminal::Complete::act (this=0x7fffffff9550, 
    str=...) at
#7  0x000055555555bc51 in emulate_terminal (fd=<optimized out>)
#8  0x000055555555dbc5 in main (argc=1, argv=<optimized out>)
Copy link

@cgull cgull commented Aug 21, 2015

I see it with printf '0\e[1J\xcc\xb4' on OS X and the 1.2.5 package distribution. Lunch & work now, more analysis later...

Copy link

@keithw keithw commented Aug 22, 2015

Thanks for fuzzing Mosh!

I think tentatively the solution is going to be to get rid of the failing assertion ( -- it's obviously not true that the only time we need fallback processing (combining character without a base character to combine it with) is for the first cell of a line, since sequences like J and K can erase the combining cell.

Copy link

@andersk andersk commented Aug 24, 2015

I set up my own afl-fuzz run over Complete::act, Complete::get_fb, Display::new_frame with asan and hardening. It found three copies of this assertion failure within 20 minutes. After removing line 122, it ran overnight without finding anything (17 cycles, 67M execs). So the good news is, this is likely the only problem to be found so easily.

Copy link

@kcwu kcwu commented Aug 24, 2015

I have the same observation. No issues found after removed line 122. (27 cycles, 180M execs)

Copy link

@keithw keithw commented Aug 24, 2015

I'd love if you fuzzed the network protocol too (maybe after removing encryption and compression to make it easier).

@keithw keithw closed this in 7ec19a5 Aug 24, 2015
Copy link

@cgull cgull commented Oct 18, 2015

@kcwu, @andersk: could either of you share your Mosh configs? It's not exactly obvious how to get afl-fuzz to run it usefully.

Copy link

@RichiH RichiH commented Apr 10, 2016

@kcwu, @andersk: Reminder-poke

Copy link

@kcwu kcwu commented Apr 11, 2016

This is simplified from src/examples/termemu.c

#include "config.h"

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>
#include <assert.h>
#include <wctype.h>
#include <iostream>
#include <typeinfo>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/time.h>
#include <exception>

#include <pty.h>
#include <util.h>
#include <libutil.h>

#include "parser.h"
#include "completeterminal.h"
#include "swrite.h"
#include "fatal_assert.h"
#include "pty_compat.h"
#include "locale_utils.h"
#include "select.h"

const size_t buf_size = 16384;

static void emulate_terminal();

int main( int argc, char *argv[] )
  fatal_assert( is_utf8_locale() );

    try {
      emulate_terminal( );
    } catch ( const std::exception &e ) {
      fprintf( stderr, "\r\nException caught: %s\r\n", e.what() );

    if (getenv("AFL_PERSISTENT")) {
        goto retry;

  return 0;

static void emulate_terminal()
  int col = 80;
  int row = 24;

  /* open parser and terminal */
  Terminal::Complete complete( col, row );
  Terminal::Framebuffer state( col, row );

  /* open display */
  Terminal::Display display( false );

  swrite( STDOUT_FILENO, );
  bool initialized = false;

  while ( 1 ) {
      /* input from user */
      char buf[ buf_size ];

      /* fill buffer if possible */
      ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size );
      if ( bytes_read == 0 ) { /* EOF */
      } else if ( bytes_read < 0 ) {
        perror( "read" );

      std::string terminal_to_host = complete.act( std::string( buf, bytes_read ) );

    Terminal::Framebuffer new_frame( complete.get_fb() );

    display.downgrade( new_frame );
    std::string update = display.new_frame( initialized, state, new_frame );
    state = new_frame;

    initialized = true;

  std::string update = display.new_frame( true, state, complete.get_fb() );
  swrite( STDOUT_FILENO, update.c_str() );

  swrite( STDOUT_FILENO, display.close().c_str() );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.