 Okay, hello everybody. Thank you for coming and welcome to my talk about proactive bug finding And taking advantage of the Debian infrastructure to find new bugs a quick summary. I will first talk about finding bugs in the sources without Trying the software or having a look at the compiled side of the the software I will Then Present a few tools to find bugs in binary packages and eventually finding bugs at runtime from the binaries We just built from the package so first bugs in the sources You have several ways to find bugs in the sources first you can do full audits or the source code Which is quite expensive requires time skills and sometimes very expensive tools So it's really only worth it if you have a lot of time and if you're auditing very critical components of of your system There is a Debian security audit project Which I don't know very well, but it exists and What it does is certainly useful But you have way ways to help as well finding bugs in faster ways Spending less energy on on finding these bugs doing cheaper faster and more automated checks for for bugs So it's really not perfect. We still need these security audits Which which go far beyond what can be done with with automatic tools But grabbing the code is really cheap and you can find many many things like that So here's an example of a common bug that only started appearing when people stopped using only the I386 architecture so this function takes a pointer to a buffer of of Charles and writes an integer in the first bytes of this Of this buffer. So what it does is create a Temporary pointer to an int and writes the integer into the first Into the first address of of this new pointer so this usually works on on a standard PC it works because even if the address is not aligned you have traps to to detect that it's not aligned and it often works quite well, but It may crash at random on other architecture that do not support unaligned reads and writes and When you build this code GCC emits a warning saying, oh you have two different pointers. You cannot do TMP equals buffer here So this is a bug you can find just by looking at the at the build logs and the GCC warnings You may want to fix the warning by doing this Just saying oh, okay buffer is not the right type. I just say it's Pointer to an integer instead. So GCC stops complaining and you don't see the warning But it's exactly the same code and you have exactly the same bug But this time you cannot detect it from the GCC warnings However, you have a chance to find this by gripping the code for for int or unsigned int star which is Well, it's not always a bug to to cast something into a pointer and to an integer But it's it's the kind of stuff you want to check in your code You can also fix it this way by using mem copy Which cares of the unalignment issues because it's copies byte by byte There's a new problem here, which is using for as the size of the data you're copying because an Integer doesn't always have four bytes. It depends your your platform So it's not really slower because GCC will inline the the mem copy call, but on Certain architecture, it might only write half the integer Well, the list of architecture I gave is I think wrong because int is always 32 bytes on these architectures, but Just assume I put long int into instead of int here and It's it's still good. So this cannot be automatically Automatically found because you cannot just grab for the number four Well, in fact gripping for the number four in some specific parts of your code or a very old code That used to work only on on DOS or windows Can find some of these of these bugs just looking for the number four so the One of the correct ways to do it is to use size off because you know that size of uses The right size for for the integer and copies the right amount of bytes You should use The STD int header if you are if you need to to deal with integers of specific sizes If you need an integral that's specifically 32 bits You should use in 32 t which is a new c99 Type, but which you can use in c89 as well using this header so This can be useful to port the applications, but let's go back at finding the bugs in the first place so auditing in an automatic way means auditing the whole source code of the the whole archive, so You if you want to do some something like this You need to get a very big hard drive because unpacking the the whole archive the whole Debian archive is very very big It's more than 100 gigabytes you You need to to make sure also that you unpack all Exactly everything that's in the archive sometimes you have a turbo in a turbo and then again a turbo in this So you may miss some files if you just deep package unpack your your files and Then grab through the code. That's all so be careful the file names You're grabbing for because a c++ file is not always a dot cpp file. They have a lot of different names and upstream likes to to wall Think of very original names for for their files Or you can just grab for everything because you may find I don't know automatically automatically generated code in the upstream table or self-generating stuff that uses I don't know shell scripts to generate see or stuff like that you You can't imagine what what you can find Well, this is going to output a lot of craft if you grab for for specific see or c++ chunks of code so you need to I don't know Maybe go through the first 100 packages and see all the the false positives you may have and find ways to To grab dash V these false positives and so it's notes. It's not a magical solution to to find bugs but a few reg X can you can Really get you to very Suspicious suspicious code that really needs to be either a written or fixed when it's really a bug There's also this nice tool, which is Google code search Which uses reg X's so you don't have to unpack the whole archive. It's already on the Google servers It's very very fast. You can use very complex reg X's with it and it it goes through the whole Google code search archive. I don't know how many packages they have Where they get exactly their software? They don't they don't have all that they been has but they have more stuff That's not in Debian like Windows software and so on So it's a good complement to it because you can find Weird structures in in some code. That's not in Debian and then look for it in the Debian archive using different reg X's and so on However, it doesn't do multi-line search. So you you cannot Look for for C state statements split on two lines. That's a very Very nasty limitation. I hope they will find ways to to work around around this But it's still a very useful tool So that's an example of a Google code search query. I simply search for Insta for cast into an Insta followed by something saying char So what you see are probably not bugs But they are very very Nasty pieces of code basically they are casting a pointer into a car star and Then adding some offset and then saying the resulting pointer is a pointer to an integer So they might have the offsets right, but you're never sure and You can be sure that by running through the eight thousand Results of this query you are going to to find very nasty bugs So we do have tools in Debian to do more clever Static analysis We have rats which does a lot of different languages see C++ PHP, Perlin, Python It's not very clever in the sense that it understands what different functions do to each other Other and so on but it still can find very interesting bugs We have P scan which does only see and a bit of C++ and focuses on Format string vulnerabilities and general bugs We also have specialized Tools for different languages JLIN for Java and Pychecker for Python And whether there are many many more that are not in Debian that you may want to try and Maybe package for Debian if you will find them useful So just Google for static code analysis to find more software like this I'm going to talk about compiler warnings now Because the first bug I showed issued a compiler warning when the when GCC noticed that the two pointers were not the same type So what do compiler warning tells they don't say there's a bug in your software They don't say your code is illegal though most of the time. It's really illegal, but GCC just says okay You meant to do this. I am going to assume that it was alright to To write what you what you did. I'm going to assume you meant this and Sometimes what GCC assumes is not what you wanted to do with your code So usually they are ambiguities in your code and GCC says okay This is my default way to handle your ambiguity, but please try to clarify it and disambiguate your code If they are if there are warnings they're here for a reason and you shouldn't understand what the warnings mean Before trying to fix them. That's the second example. I gave just adding in star to your code Doesn't make the bug disappear. It only makes the warning disappear. That's not what you want to do Also you are in a privileged situation where you have access to a To our whole build infrastructure with many many different architectures with different and yenness different pointer sizes we're going to have different kernels to try them and so on you can already have a look at the at the herd Build logs which have different system headers and can make your your build different You can have access to the K3 BSD build logs already and all of this your upstream Is probably not aware of it and probably probably doesn't care, but we as Debian Do care about other architectures. So we need to have a look at our build logs Some porters are already doing this. They are gripping the the build logs for common errors and common Portability issues, but you as the maintainer might want to have a look at them as well. So How do you activate compiler warnings GCC doesn't always emit warnings for everything usually It's emits warning for very dangerous stuff by default then you have different other flags like Tash W all that emits warnings for for very common errors in your code Well errors or ambiguities like I said There's also Tash W that emits a few additional warnings that may I know you if you're dealing with very old code or very I don't know very special code constructs that are perfectly valid, but That emit warnings for some or some reasons You have also a lot of other additional useful ones that you cannot activate with a magical flags that would activate all warnings, but For instance tests between signed and unsigned integers Usually you don't mind that if you compare i and j and i is an int and j is an unsigned int They use there's usually no problem, but if you If your integers reach very high values and or very negative values and reach the boundaries of What your system has for integer values, then you might run into trouble so it's It's always good to have a look at these warnings So the way to catch these warnings in is not well the way the way to fix these warnings It's not always to cast the int into an unsigned int But it might be to make sure that max int isn't reached by your integer. Well Just don't blindly cast things into another in C and Believe that if the warning disappears your problem disappears as well and the problem is that upstream doesn't always The always activate the warnings you have very weird build systems that do not Allow you to change C flags and sometimes it's just a matter of adding a flag And I'm going to show a few ways to activate warnings when compiling your software So for auto tools packages, it's really easy. You just export C flags when doing the the configure step That's what you do in Debian rules and usually it works perfectly Sometimes upstream didn't use the the auto tools the way they were supposed to be used Given how complicated they might be it's not always upstream's fault, but sometimes you have to export C flags or another Another variable like AM C flags or very weird stuff just to get the compiler warnings to be to be activated If your package doesn't use them you might have to edit the make file or whatever build system It's using it's sometimes very very difficult to add a specific compiler flag to your application You may need to edit by hand all the makefiles of your project And sometimes also and this is what leap tools does Or auto make well a combination of the two the output of the compiler is sent to dev nil this is for instance because When you build a library with leap tool you build both a shared and a static library and The leap tool developers assume that if the static version of the library Compiled okay, then the shared version of the library will compile the same way So there is no need to output the warnings in the In the static version of the library, but sometimes This compilation step is slightly different there are Sometimes assembly constructs like that change depending on the architecture for instance on ETH on i386 you have one register that is always used for a specific purpose in libraries and So this register is not available and if you are using assembly code in your in your program then this might fail at the compilation step and GCC doesn't say anything and just exits or sometimes It doesn't exit and you don't know what happened to your build. There's a missing file and so on So redirecting and the compiler output to dev nil is not usually a good idea I wrote a small library to Overwrite this which simply wraps every call to exact VE or exact VP and anything that may call GCC or those build application does and checks what flags are given to GCC and Adds adds the missing flags if you want. I don't know Dash w to be added it adds them it can remove flags and detects when The the output of your program is redirected to dev nil which you do not want usually So here is an example of what it does It's an excerpt of a build log So we can see at line 6. There's a make wrap warning saying oh This version of GCC was called with only dash w all I'm adding dash w and dash w sign Compare and you can see at the end of the screen. There's a lot of warnings about Sign and unsigned comparisons between between integers So it's just an example of what it does and you you need just need to export LD preload before Calling the package build package and it will do everything for you and Well, your build will have more warnings Sometimes they're really useless and you don't want them, but you may run into new bugs by by using this technique So this is an auto tools using programs. So there was no No real Interest in doing this, but if your package uses a null make file or sconce or Things you don't necessarily understand then it's a very easy way to to change compiler flags I'm going to talk about other compiler warnings now You have one very very dangerous warning that you should almost always fix Which is implicit declaration of function whatever? This is problematic because when you do not declare a function prototype without Before using it GCC we will make a lot of assumptions about what this function does for instance, it will assume that the function returns an int and If your function actually Returns a pointer Then on on a PC machine on a 9 3 8 6 architecture Everything would be all right because an int and a pointer have the same size So well the type is different, but the contents of the register at at return time are the same So no bug here, but if you do the same on AMD 64 then your program will crash because The the cold function will return pointer on 64 bits and The calling function will just read the first 32 bits of that return value because it thought it was an integer So you end up having a lot and lots of bugs with with functions returning in with returning pointers on 64 bits architectures That's also a problem with your arguments because The compiler will assume that the type of the arguments you give to your function Are the times that are expected by the app by the by the function and This is not always the case. You may expect the the compiler to cast a long int into an int or any any kind of implicit cast at call time and But this is not always the case and the compiler cannot guess whether it was a long int or an int that was expected So on on a PC again, there will be no problem because int and long int have the same size But again on a 64 bits architecture You will call your your function with the wrong arguments and well, you're probably going to crash another common warning is Suggest palm thesis or an assignment use as truth value. This is usually Either when you forget the double equal in an if test or When you meant it but didn't add an additional pair of palm thesis So it's not really a bug because it's probably exactly what you wanted to do and The c-standard doesn't say anything about doing if x equal 5 but if you start ignoring this warning and one time just Forget to do the double equal where you meant double equal. Well, then you have a bug So this is the kind of warning that are worth fixing because when they really appear you will you will notice them and Just not say okay. I just I just didn't want to put the double palm thesis and I don't care another warning is warning about uninitialized variables Which you only can get when turning on the optimization flags when you're built. So remember that the optimizer will run many checks on When and until when variables are used Initialized and when they can they can use an additional register to replace this variable That's no longer used and so on so the optimizer is doing very clever stuff to To deal with your variables and it can detect when you're using a variable that you have not assigned before so Do not assume that variables that you declare on the stack are Zeroed or have a default value or anything because they do not so always initialize your variables if you need them and very usually this is this is a bug and Needs to be fixed at the code level So why use the build the logs because they are all in one place. You can download them You can grab for them in a very Quick way I was told it was one gigabyte of data Approximately for one architecture So well, it's a lot of data, but you can do a lot and a lot of tests on them and Well, what's very good as well is that they have all the architectures and as I said architectures vary a little bit and this little bit is Very frequently what causes the bugs to appear. So it's very good to have them available One problem is that when you upload a package the build these do not get well the build D Dot them in dot org site does not get your build log So actually we have all the architectures except yours and yours might have been important as well. So It would be nice to have these build logs as well on the same site Just to to make things consistent Now I'm going to talk about bugs in the binary packages And a few tools we already have in Debian to check for for bugs and check packages Which you hopefully you'll know already The first of these is lintian so lintian check source and binary packages and Well, most of the tests is does it does Is about the policy so it checks whether packages comply with the policy and it outputs a machine readable output which can be then used for data mining and Getting a list of packages which are which have the following warning issued by lintian and so on and We have a service on lintian.debian.org that outputs everything lintian says about about the current archive You can add your own checks to lintian. It's not very difficult What you just need to understand what lintian does when you give it a package it extracts it in in what it calls a laboratory and gathers information about what is in the package for instance Whether a given file is a script if it's an executable it's runs up stamp on it and gathers information about symbols all the all the usual stuff it then needs to to check to see whether a package is compliant or not and Well, then it runs the checks so to create a check You need to know a bit of pearl because lintian checks are pearl scripts that implement one single function which is run and It uses all kind of information from the lintian from the lintian laboratory and lintian exports a few functions that you can use to ease the task and You can easily write a check that runs on all scripts that are in the lintian Meta information and Well check whether I don't know you can check for Specific functions that you do not want to call in a shell scripts There's a check for bash isms in in shell scripts that you can Use to do your own check I really don't know what you could be looking for but but it's very easy to write a check for this and Then you can run lintian on the whole archive and you will have your own check Applied to all the package You should also create a description for the text that you want to add to lintian So I have a few ideas about improving lintian.debian.org that I Discussed a bit but did not really Really discussed with the lintian.debian.org maintainer and it's not it's in a Too early stage of completeness to really be useful, but what I would like to know is for instance There's a warning in my package in which version did it appear So what cozy what caused the The warning to appear was it a new epsom version? Was it something I changed in the compiler flags? Was it something? That I changed by changing the relationship with another library or something like that I would like to know as well which other packages have had this tag appear at which time maybe it was just a new lintian version that added the tag and then Why what did the other package maintainers do to fix this tag? So for a given tag I I will get the information on how to fix it The practical information because sometimes just the the tag Descriptions doesn't tell me what to do to fix the tag and then you can then use it To fix other packages and help the people who have this this tag at one time So one idea would be to use more for for gathering the information because Well, it's already a multiple purpose Determining Framework for everything we have in the archive and around it Have a proof-of-concept code that doesn't appear because the screen is too small, but I have a screenshot at least All right, I don't know if it's readable. Well, that's the usual lintian Output that you get from a given package, but this one shows different versions of a package and in red you see tags that have appeared in a given upload and in green You see tags that disappear in the next upload of this package For instance the first tag package uses deprecated the help of compact version 3 Was how well it disappeared in the next upload. So well, this is an easy tag and you should know how to fix it, but If you have a more complicated tag, you may know that it was in the the upload 2.1.4a-5 that the the tag was fixed So this is a valuable service that I think we should have Because I have lintian warnings that I am unable to fix because they require a very very difficult modifications in the code about A self-modifying stack in the libraries and stuff like that. So it requires time and If I can get my hands on a on a package that fixed this morning, then I will get more information about how to fix it So there is another tool very much like lintian, which is called linda It really does the the same thing as lintian except it has different checks. It's written in another language and Well, you really should use both just in case because I don't think one or the other is superior to to the other so just use both and if you want to implement a test and You use you're more familiar with Python than with Perl. Well, just add your check to linda Because it does exactly the same thing. It has Python checks Well, meter data about long descriptions of the tags and what they do but really they are similar in how they are implemented except Linda is more object oriented but You should really be able to write your own checks if you are familiar with Perl or Python so why would you want to create new checks because Qa is not always about the policy. You want to have different checks for for packages You want to initiate the transition and you want to know how many packages are affected by this transition. Well, you can just add a tag to lintian and run lintian and Well, it's easier than downloading the archive and writing your Your own script to check them usually because lintian does all the unpacking for you. It's not always that easy but But using the existing tools to check this is a gain of time So a few examples you can you could check for packages which have a menu a menu file, but no desktop file Yet they are desktop Applications, so you might want to create one packages that have a menu file, but no icons You may want to check for packages lacking an xvcs version control information field in the control file because you know these packages is Managed by a revision control system and it would be nice to have this information in the package You can check whether package ignores or Just handles the deb build options flags in a weird way or just just ignore them or What I did was extract font copyright information from all the fonts that we have in Debian to check whether a few a few fonts were Deplicates or non-free or something so Adding checks to these tools is is very easy and you should consider it when you have something to do on the whole archive There's a recent tool called pew parts that you may want to to check as well I know I discovered it pretty late, so you might maybe not know about it well some of you What it does is install a very Very minimal system with almost no Dependencies and tries to install your package installing all the dependencies and Predependences checks whether it removes properly purges properly. You can check for upgrades. You can check check for mass upgrades like an edge to lany upgrade you can check for stuff like that and You really should use it because More than once you see packages that That have very grave bugs that you can find with pew parts and they are in the archive anyway Because many of us do not test their packages because they just did a minimal Change to it and didn't think it was worth testing it, but sometimes it is so try to use these tools Okay, pew parts can be extended as well I must admit I do not know how But I have a few ideas of what could be done to pew parts to make it Even more useful for some packages. For instance, you could corrupt files in varkash Which are not supposed to be well corrupted, but sometimes the information you get in these files can come from corrupted website for instance if your Apped cache is broken. You may try to see what happens if you try to install packages and so on You can mass check package installations with bnsh set to bash or zsash or other other shells that claim or are remotely Compatible with the POSIX standard Well, they're not necessarily bugs, but if it's a one-liner to fix your package so that it works with Bnsh set to zsash. Well, you you really Want to fix this Okay, last time I'm going to talk about runtime bugs bugs that you can find once the application is compiled and you can run it Sometimes your software has a test suite in it. You can Run make check or make test or whatever after having compiled the application It's usually not Activated by default. So you have to run it by hand or add it to Damian rules or something like that If it can be activated at build time, well, please do it if it's not too much Too much load for the auto builders Maybe disable it for m68k or any slow architecture, but If you're building your application for AMD64, try the test suite Maybe don't make the build fail if the test should fail but just to get the information of whether it works at all and well, if you're tired of building your own package times and times and Your you want to disable the check you might want to implement the build options equal no check flag That was proposed in a recent bug so you just export this variable and your Your package will build without doing the test Try also to remain cross-buildable Check that the target architectures the target architectures matches the current architecture so so that you can really run the The binaries that you built because they do not necessarily build on your architecture when you're building a package and Well, if there is no test suite you may want to create one yourself I agree. It's not really your job as a package maintainer but sometimes you you see weird interactions between one package and one other package and Well, you might want to write a test for this. Maybe well It's just when one library is installed It has weird effects to to your software or it used to have you can check for Regressions with a few lines of additional make file code. So think of it if you think it might be useful One other common practice when you do not have a test suite is to use fuzzing the idea of fuzzing is to use known data or Random data and alter it and feed it to your software this has this is very trendy these times because People are tired to check for bugs by reading source code and they're using fuzzing more and more to easily find bugs because it's really the The lazy programmer solution, but it it works it exposes bugs in a lot a lot of Fuzzer applications which get that data from the web or from emails and Every crash that's caused by an application reading an email or reading something from the web is a potential vulnerability and a potential attack vector So these bugs should really not be underestimated. I wrote a small fuzzing application that I will show you It's really simple simple. It's called surf and It uses LD preload to first data So you just run your application with the usual flags into surf and it will corrupt Depending on what exactly you want to corrupt You can choose which files are corrupted whether you want to to corrupt network input or a DVD And then it checks what happens when you run the application It checks for crashes segfolds our boards and so on it checks whether your application Starts eating all the memory whether it entered an an infinite loop and doesn't exit and so on and The behavior is reproducible. So you can tell Zoff well try a Billion random seeds and you go to sleep and the following morning You get a report from Zoff saying well the following seed cozy cause the application to crash This one caused it to run forever and so on and you can reproduce the bug You can generate a file that when fed to the application without using Zoff will crash the application the same way or almost the same way sometimes it doesn't work for complicated reasons, but It's really easy to use and and you find bugs very very quickly So this is an example of Zoff running on the cat program It just cats a file and you see there is one corrupted Byte shown by the green hour This is by fuzzing 0.1% of the the output bytes of the input bytes Sorry, so what does of those is run the program cat and intercepts open f open and all those functions and then read write and so on and When it when it intercepts read it will corrupt what the read function writes into the buffer So this is the same call with 3.8 of the bits corrupted So you see the the output is a lot different and becomes Almost unrecognizable, so you can fine-tune the the randomness not the randomness the fuzziness factor to To check how your application behaves when when fed with this kind of data It has a debug mode that's that tells you what finds your application opens and when it reads data and so on so This is an example on the file program. You see that the file program opens ETC magic and One file in user share before opening. It's the argument you gave it you gave to it So you do not want to fuzz what's in ETC magic or in user share because it's supposed to be trusted data You can check for bugs as well, but they are not security bugs because What's in ETC magic isn't supposed to be changed by an attacker or if the attacker can't change ETC magic It's not going to to trick you into running fine. It will do more nasty things So you need to tell us off to ignore what's in ETC for instance and have it only open bin LS which is the file I gave it as an argument So that's how you do it dash E and you ignore It's They are regular expressions, so you can ignore Dot PNG files if you do if you only want to fuzz JPEG files or stop like that and you see the result with virus random seeds from zero to five and you see that sometimes File sees a real elf binary. Sometimes it only sees data random data and well It doesn't crash in this example, but you can get fired to crash with well you could Cause fire to crash with a specially crafted files So even if it crashes you need to do something afterwards This is an example of give to PNM What the arguments says are try random seeds from zero to one thousand and change the the fuzzing Amount between zero dot one percent and ten percent and just Try give to PNM image dot give 1000 times and it exits after 19 tries and says well at Try 19 there was a segmentation fault and That's the Well, it just exits have after having found a segmentation fault So then you can use surf to create a new file with the exact same parameters which is seed 19 and the random factors which are the same and Save it to a new gift file and then it causes give to PNM to crash immediately So you do not need to to have surf to debug the this problem afterwards This is another example with entire word. I Think that entire word is activated by default when you install it and have not so if you receive a Dot doc document in an email. I think entire world entire world tries to To open it and display it in text mode. So be careful There are many many ways to crush entire world. It seems I Don't know whether any of these are security issues, but Be careful. You see a lot of segmentation falls heap corruptions and memory exhaustion, so I Wouldn't trust entire world at this time There's a lot of additional fuzzing software available. There's one very interesting one called ashore which is more for framework than of a real application, but It has many connected applications and it's very very clever. It has a lot of file parses for different file formats it can It if it has knowledge of the file format, it can try different values in a given part of the file format for instance, if You your file format uses checksums like the PNG format then a tool like surf is useless Because you change one bit and the checksum is wrong and there's really no chance that by corrupting the checksum as well, you will get the the proper file so For some complicated formats you need a tool that is format aware and Hasho is one of these There are many other fuzzing application and more and more are appearing nowadays You have specific SQL injection fuzzers for web applications. You have LDAP fuzzers You have IP stock fuzzers and so on so just look for fuzzing first testing or fault injection on Google And you will find all the tools you might need to to check your applications So now fuzzing as a test suit. Why do this? Well, I already explained it a bit. It's a very cheap way to have a test suit You can just build depend on the fuzzer and add Add a one-liner to Debian rules and check your application after it has built This way you can Benefit from all the architectures we have because we already saw that all applications do not behave the same way on all architectures So it's really a benefit to to have this build architecture Please be reasonable do not run a test suit that runs for 24 hours on on on AMD 64 because a few build maintainers will hate you and also Think before making the test suits cause the build to crash well to fail because It's a random build failure and it's probably not policy compliant so and Maybe it's a very isolated bug. That's not really critical So you shouldn't be making a build fail just because one of the Checks in the test should failed But it's up to you. I don't know what kind of software in your package and it might be wise to To cause the bill to fail just in case but really think of it and know what the checks are about without making Before making the the build fail you can also Have test suits for GUI applications even of in a in a build demon You just need to install the xvfp Package which is an X server for the virtual frame buffer Feature of the kernel and it just launches an X server in memory and Displays its contents in memory and it doesn't need a screen to work It just needs the the minimal amount of software that you need to to install to get your application running So you may want to try to to have test suits for GUI applications as well It's complicated. You need to make sure that your application has a way to exit after a time Otherwise your build will just hang but it's a thing work. It's a thing worth having in mind So I'm finished if you have any questions, I will take them I don't know how much time remains is remaining, but I don't think we have more than five or ten minutes Not yet. No, none of them As a maybe for people who are interested in lynching is tomorrow a buff about lynching So people want to know more about writing checks and can attend. I think it's tomorrow at two o'clock and And tomorrow at two o'clock. Yes, I don't know where but it's on schedule Thanks. Okay. Well, if there are no additional questions. Thank you very much See you