Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Time tested php with libtimemachine


Published on

Dynamically adjust system time used by PHP with libtimemachine for advanced testing.

Published in: Technology
  • Be the first to comment

Time tested php with libtimemachine

  1. 1. Time Tested PHPAdvanced testing techniques with libTimeMachine Nick Galbreath @ngalbreath Vince Tse @vtonehundred 2012-07-19
  2. 2. Follow along or get the latest version at: NDc5mK
  3. 3. Time HappensWhile it should be avoided as much as possible, sometimes"time happens" and applications need testing based onsimulated time.• Financial applications (e.g. simulating ad spending and budgeting)• Security features (what happens when the cookie or auth token expires?)• System testing (what happens on leap year? day light savings time? 2038?)• Anything that runs periodically ("on the hour")
  4. 4. PHP Time Sources• $_SERVER[REQUEST_TIME]• time()• microtime()• gettimeofday()• Single argument of date(fmt) (equivalent to date(fmt, time())
  5. 5. Using $_SERVER[REQUEST_TIME]• Available in all SAPI contexts (mod_php, CLI, CGI, FPM...)• Created once at time of request• "Lowest Cost" -- array lookup• Easy to spoof in unit tests• Cant spoof for functional tests• Cant use it for timing
  6. 6. Passing as Argument• Dont call time() et al directly in a function but instead pass current time in.• Allows unit testing• Follows dependency injection best-practice
  7. 7. But what if your code isnt or cant bestructured that way?
  8. 8. Time Travel with libtimemachine! Changes the system calls that PHP uses to get the current time • time (defined in <time.h>) • gettimeofday (defined in <sys/time.h>) • clock_gettime (defined in <time.h>) and allows you to change them backwards or forward, relative or absolute.
  9. 9. LibTimeMachine• Use some secret loader sexiness to change the underlying system calls. (see man ld-linux for details)• Works on Linux systems• Works on Mac OS X (only tested on 10.7.4)• Sorry Windows• (not sure about FreeBSD)
  10. 10. Plug and Playgit clone git:// libtimemachinemakesudo cp [ /lib64 or /lib ]sudo ldconfig
  11. 11. To use!• libtimemachine reads /tmp/libtimemachine.conf (or whatever file you want using the LIBTIMEMACHINE_CONF environment variable)• Single number controls how to adjust time• If starts with "-" or "+" then current time will be adjusted by a relative amount.• If "just numbers" then the time is fixed with this value• If "0" or missing, then use current time
  12. 12. PHP CLIJust add LD_PRELOAD=libtimemachine.sobefore php on the command line$ php -r echo date("rn");Mon, 28 May 2012 23:03:38 -0400$ # go back one year$ echo "-31536000" > /tmp/libtimemachine.conf$ php -r echo date("rn");Sun, 29 May 2011 23:03:49 -0400$ #winning
  13. 13. PHP 5.4 Built-In WebServer This is the easiest way to go!$ dateMon May 28 23:27:19 2012$ echo "31536000" > /tmp/libtimemachine.conf$ LD_PRELOAD=/lib64/ ./php -t ~/root -S 5.4.3 Development Server started at Tue May 28 23:29:19 2013Listening on root is ~/rootPress Ctrl-C to quit.[Tue May 28 23:29:22 2013] [200]: ~/time.php Command line CGI works similarly
  14. 14. Apache mod_php Debian / Ubuntu• Install in /lib64 or / lib depending on your OS.• (for good measure also do "sudo ldconfig")• /etc/apache2/envvars controls the apache and workers environment. Add export• sudo /etc/init.d/apache2 restart
  15. 15. <?phpheader(Content-Type: text/plain);date_default_timezone_set(UTC); //if you need itprintf("REQUEST_TIME : %sn", date("r",$_SERVER[REQUEST_TIME]));printf("time() : %sn", date("r", time()));printf("microtime() : %sn", date("r", microtime(TRUE)));printf("date(r) : %sn", date("r"));printf("gettimeofday() : %sn", date("r", gettimeofday(TRUE)));//print_r($_SERVER);
  16. 16. Back One Day!$ dateSun, 27 May 2012 19:35:41 +0000$ echo "-86400" > /tmp/libtimemachine.conf$ curl : Sat, 26 May 2012 19:35:54 +0000time() : Sat, 26 May 2012 19:35:54 +0000microtime() : Sat, 26 May 2012 19:35:54 +0000date(r) : Sat, 26 May 2012 19:35:54 +0000gettimeofday() : Sat, 26 May 2012 19:35:54 +0000
  17. 17. apache mod_php RedHat/CentOS• Disable SELinux: in /etc/selinux/config set SELINUX=disabled• put in /lib64 or /lib depending on your OS.• (for good measure also do "sudo ldconfig")• add to /etc/sysconfig/httpd export• And then...
  18. 18. Fail on Apache +mod_php + CentOS 6.2• SELinux removes LD_PRELOAD• Even though we disabled SELinux, it appears the linker isnt getting LD_PRELOAD• mod_php is an shared library that loads shared libraries. hmmm• I suspect a bug in the OS? Or maybe mod_php is compiled differently.• Use PHP 5.4s built-in web server instead for testing.
  19. 19. Future Work• Apache + PHP CGI (does anyone do this?)• nginx + PHP FPM (the new hotness)• Figuring out what is going on with CentOS• Testing on mysql server.• Packaging
  20. 20. Detecting libtimemachine• Look for existence of /tmp/libtimemachine.conf• Shell out and use "date +%s" and compare to time()• Use Apache mod_env and add PassEnv LD_PRELOAD to let PHP see the environment variable
  21. 21. Evil• Can this technique be used for evil?• Oh yeah.• type "LD_PRELOAD rootkit" in your favorite search engine for details
  22. 22. Mac OS X Notes• Only tested on 10.7.4• Mac OS X uses dyld for linking and works different than gnu ld. See man dyld for details.• Instead of LD_PRELOAD, use: DYLD_INSERT_LIBRARIES=./libtimemachine.dylib• If that doesnt work, add DYLD_FORCE_FLAT_NAMESPACE=1
  23. 23. Gotchas• if you globally set LD_PRELOAD, export then everything you do might be time shifted (to undo unset LD_PRELOAD)• Your application might run a bit slower since every time lookup requires reading /tmp/libtimemachine.conf
  24. 24. Thanks! Nick Galbreath @ngalbreath Vince Tse @vtonehundred