/*
 * KSR[T] Presents: INSTRUCTOR 1.0
 *
 * This program attempts to execute every 32 bit instruction.  It is
 * useful for people who are trying to find hidden features, or hidden
 * bugs in their hardware or operating system.  
 *
 * Internally, KSR[T] has used this application to discover several 
 * problems (so far two Alpha/Linux/MILO problems and one x86 problem). 
 *
 * Version 1 has several limitations which will be addressed in Version 2:
 *
 * 1)  This application is less predictable on x86 architecture. This is
 *     because x86 has variable length instruction sizes/prefixes.
 * 2)  Application provides less detail in order to keep CPP 
 *     directives:C source ratio sane.  
 * 3)  We actually end up executing 0x00000000 if the instruction we
 *     created was valid.  If 0x00000000 is valid, random stuff will
 *     be executed.
 *
 * Running this will definitely:
 * 1)  Bog down the machine it is running on,
 * 2)  Generate a gigantic log file.
 *
 * and possibly:
 * 3)  Lock the machine up good.
 *
 * Run this at your own risk.
 *
 * Certain OSs (Linux, for example) reset the signal handler 
 * for a particular signal, after that signal is raised.  You can compile 
 * with -DFUNKY_SIGNALS to work around that.
 *
 * Note on : instructor.c vs. crashme.c
 *
 * Benefit of instructor:
 * 
 * o Can validate every instruction in a reliable manner
 *   
 * Benefit of crashme:
 *
 * o Can discover flaws involving multiple instructions (i.e. Cyrix Coma bug)
 *
 * LICENSE RESTRICTIONS
 *
 * This source code may not be used in a closed source tool, or any 
 * application that is sold, be it shareware, commercial or otherwise.
 * It also must not be used in any for-profit manner, including professional
 * services engagements.  Manufacturers of processors are also prohibited
 * from using this application.
 *
 * If you fall into one of the above categories, contact KSR[T] 
 * <ksrt@ksrt.org> before using this application.
 *
 * David Goldsmith
 * <daveg@ksrt.org>
 * http://www.ksrt.org
 * (C) 1997, 1998, 1999, 2000 KSR[T]
 */

#include <stdio.h>
#include <setjmp.h>
#include <getopt.h>
#include <sys/signal.h>

#ifndef NSIG
#define NSIG 65
#endif

char cur_inst[1024]; /* someone email dhg@8096-bit.ksrt.org when instructions 
                        get larger */
int sig_caught;
FILE *log_fp;

#ifdef FUNKY_SIGNALS
int last_sig;
#endif

unsigned long instruction;
void (*func_ptr)() = (void *)&instruction;
jmp_buf jbuf;

void
the_end()
{
    fprintf( log_fp, "+ We have run through every opcode\n+ hooray for the chip designer!\n");
    fclose( log_fp );
    exit(0);
}

void
sig_catcher( signo )
int signo;
{
    switch( signo )
    {
        case SIGILL:
             fprintf(log_fp,"Instruction 0x%s generated SIGILL.\n", cur_inst );
             break;
        case SIGSEGV:
             fprintf(log_fp,"Instruction 0x%s generated SIGSEGV.\n", cur_inst );
             break;
        case SIGALRM:
             fprintf(log_fp,"Instruction 0x%s looping (SIGALRM).\n", cur_inst );
             break;
    }
    fflush( log_fp );
    sig_caught = 1;
#ifdef FUNKY_SIGNALS
    last_sig = signo;
#endif
    longjmp( jbuf, 1 );
}

void
set_sigs()
{
   int i;

   for (i = 0 ; i < NSIG ; i++ )
      signal( i, sig_catcher );
   signal( SIGINT, SIG_DFL );

}

void
print_usage( name )
char *name;
{
   fprintf(stderr, "Instructor v1.0\n%s [-f logfile] [-h] start_instruction\n"
               "    start_instruction is given in hex defaults to 0x00000000\n"
               "    logfile defaults to stdout\n", name );
}

int
main( argc, argv )
int argc;
char **argv;
{
    int we_aint_even_started_yet, c; 
    char *file_name;
    set_sigs();
    
    we_aint_even_started_yet = 1;
    sig_caught = 0;
    memset( cur_inst, 0x0, 1024 );
    file_name = (char *)NULL;

    while ( ( c = getopt( argc, argv, "hf:" ) ) != -1 )
    {
       switch ( c )
       {
          case 'f': file_name = optarg;
                    break;
          case 'h': 
          default : print_usage(argv[0]);
                    exit( -1 );
       }
    }
    argv += optind;
    argc -= optind;
 
    if ( argc == 1 )
       instruction =(unsigned long) strtol( argv[0], (char **)NULL, 0 );
    else
       instruction = (unsigned long) 0;

    if ( file_name == NULL )
       log_fp = stdout;
    else
    {
       log_fp = fopen( file_name, "a+" );
    }
    if ( log_fp != NULL )
    {
        do
        {
            sprintf( cur_inst, "%.8lx", instruction );

            if ( we_aint_even_started_yet == 1 )
               goto foo2;
foo:      
            alarm(2);
            (*func_ptr)();
foo2:
            if ( we_aint_even_started_yet == 1 )
            {
               if ( setjmp( jbuf ) == 0 ) /* only happens on initial setjmp() */
               {
                  we_aint_even_started_yet = 0;
                  goto foo;
               }
            }

            if ( sig_caught == 1 )
            {
               sig_caught = 0;
#ifdef FUNKY_SIGNALS
               signal( last_sig, sig_catcher );
#endif
            }
            else
            {
               fprintf( log_fp, "Instruction 0x%s was a-ok\n", cur_inst );
               fflush( log_fp );
            }
            instruction++;
        } while ( instruction != 0 );

        the_end();
    }
    else
    {
        fprintf( stderr, "- Couldnt open file\nProgram terminated\n");
    }
}

