for a university project we are implementing an algorithm capable of bruteforcing on an AES key that we assume is partially known.
We have implemented several versions including one that exploits the multithreading mechanism in C++.
The implementation is done by allocating a variable number of threads, to be passed as input at launch, and dividing the key space equally for each thread that will cycle through the respective range attempting each key. De facto the implementation works, as it succeeds in finding the key for any combination #bitsToHack/#threads but returns strange timing results.
//Structs for threads and respective data
pthread_t threads[num_of_threads];
struct bf_data td [num_of_threads];
int rc;
//Space division
uintmax_t index = pow (BASE_NUMBER, num_bits_to_hack);
uintmax_t step = index/num_of_threads;
if(sem_init(&s, 1, 0)!=0){
printf("Error during semaphore initialization\n");
return -1;
}
for(int i = 0; i < num_of_threads; i++){
//Structure initialization
td[i].ciphertext = ciphertext;
td[i].hacked_key = hacked_key;
td[i].iv_aes = iv_aes;
td[i].key = key_aes;
td[i].num_bits_to_hack = num_bits_to_hack;
td[i].plaintext = plaintext;
td[i].starting_point = step*i;
td[i].step = step;
td[i].num_of_threads = num_of_threads;
if(DEBUG)
printf("Starting point for thread %d is: %lu, using step: %lu\n", i , td[i].starting_point, td[i].step);
rc = pthread_create(&threads[i], NULL, decryption_brute_force, (void*)&td[i]);
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
sem_wait(&s);
for(int i = 0; i < num_of_threads; i++){
pthread_join(threads[i], NULL);
}
For the decryption_brute_force function (The body of each thread):
void* decryption_brute_force(void* data){
** Copy data on local thread memory
** Build the key to begin the search from starting point
** for each key from starting_point to starting_point + step
** Try decryption
** if obtained plaintext corresponds to the expected one
** Print results, wake up main thread and terminate
** else
** increment the key and continue
}
To conclude the project we intended to conduct a study of the optimal number of threads expecting an increase in performance as the number of threads increased up to a threshold, after which the system would no longer benefit from the increase in threads assigned to it.
At the end of the analysis (a simulation lasting about 9 hours), the results obtained were as follows in figure.
Click here to see the plot.
We cannot understand why 8 threads performs better than 16. Could it be due to the CPU architecture? Could it be able to schedule 32 and 8 threads better than 16?
From comments, I think it could be the linear-search pattern in each thread yields to different results for different number of threads. Because when you double the threads, the actual linear point to find in a thread may shift to a further point. But once you double again, it can not go much further due to too many threads. Because you said you are using only same encrypted data always. Did you try different inputs?
this variable is integer (so it may not be exact distribution)
^
8 threads & step=7 (56 work total)
index-16 (0-based)
v
01234567 89abcdef 01234567 89abcdef
| | |. | ...
500 seconds as its the first loop iteration
16 threads & step=3 (56 work total)
index-16 again, but at second-iteration now
v
012 345 678 9ab cde f01 234 567 8
| | | | | | . | | | ...
1000 seconds as it finds only after second iteration in the thread
Another example with 2 threads and 3 threads:
x to found at 51-th element of 100-element-work:
2 threads
| |x(1st iteration) |
3 threads
| |........x | |
5x slower than 2 threads
I am trying to use quadpack in Microsoft Visual using Intel Fortran.
From netlib I downloaded: dqag.f, dqage.f, dqk15.f, dqk21.f, dqk31.f, dqk41.f, dqk51.f, dqk61.f, dqpsrt.f, dquad.f, fdump.f, j4save.f, s88fmt.f, testf4.f, xerabt.f, xerctl.f, xerprt.f, xerror.f, xerrwv.f, xersav.f, xgetua.f.
to try to recreate the result from makefile.2.
And I get the errors:
Severity Code Description Project File Line Suppression State
Error Compilation Aborted (code 1) C:\Users\...\test_quadpack\xerrwv.f 1
Error error #6633: The type of the actual argument differs from the type of the dummy argument. [LFIRST] C:\Users\...\test_quadpack\xerrwv.f 65
Error error #6633: The type of the actual argument differs from the type of the dummy argument. [MESSG] C:\Users\...\test_quadpack\xerrwv.f 127
Error error #6634: The shape matching rules of actual arguments and dummy arguments have been violated. [MESSG] C:\Users\C:\Users\...\test_quadpack\xerrwv.f 127
These errors I get from xerrmv.f which can be downloaded from xerrmv.f line 65, 127 and 127
subroutine xerrwv(messg,nmessg,nerr,level,ni,i1,i2,nr,r1,r2)
c
c abstract
c xerrwv processes a diagnostic message, in a manner
c determined by the value of level and the current value
c of the library error control flag, kontrl.
c (see subroutine xsetf for details.)
c in addition, up to two integer values and two real
c values may be printed along with the message.
c
c description of parameters
c --input--
c messg - the hollerith message to be processed.
c nmessg- the actual number of characters in messg.
c nerr - the error number associated with this message.
c nerr must not be zero.
c level - error category.
c =2 means this is an unconditionally fatal error.
c =1 means this is a recoverable error. (i.e., it is
c non-fatal if xsetf has been appropriately called.)
c =0 means this is a warning message only.
c =-1 means this is a warning message which is to be
c printed at most once, regardless of how many
c times this call is executed.
c ni - number of integer values to be printed. (o to 2)
c i1 - first integer value.
c i2 - second integer value.
c nr - number of real values to be printed. (0 to 2)
c r1 - first real value.
c r2 - second real value.
c
c examples
c call xerror(29hsmooth -- num (=i1) was zero.,29,1,2,
c 1 1,num,0,0,0.,0.)
c call xerrwv(54hquadxy -- requested error (r1) less than minimum
c 1 (r2).,54,77,1,0,0,0,2,errreq,errmin)
c
c written by ron jones, with slatec common math library subcommittee
c latest revision --- 19 mar 1980
c
dimension messg(nmessg),lun(5)
c get flags
lkntrl = j4save(2,0,.false.)
maxmes = j4save(4,0,.false.)
c check for valid input
if ((nmessg.gt.0).and.(nerr.ne.0).and.
1 (level.ge.(-1)).and.(level.le.2)) go to 10
if (lkntrl.gt.0) call xerprt(17hfatal error in...,17)
call xerprt(23hxerror -- invalid input,23)
if (lkntrl.gt.0) call fdump
if (lkntrl.gt.0) call xerprt(29hjob abort due to fatal error.,
1 29)
if (lkntrl.gt.0) call xersav(1h ,0,0,0,kdummy)
call xerabt(23hxerror -- invalid input,23)
return
10 continue
c record message
junk = j4save(1,nerr,.true.)
call xersav(messg,nmessg,nerr,level,kount)
c let user override
lfirst = messg(1)
lmessg = nmessg
lerr = nerr
llevel = level
call xerctl(lfirst,lmessg,lerr,llevel,lkntrl)
c reset to original values
lmessg = nmessg
lerr = nerr
llevel = level
lkntrl = max0(-2,min0(2,lkntrl))
mkntrl = iabs(lkntrl)
c decide whether to print message
if ((llevel.lt.2).and.(lkntrl.eq.0)) go to 100
if (((llevel.eq.(-1)).and.(kount.gt.min0(1,maxmes)))
1.or.((llevel.eq.0) .and.(kount.gt.maxmes))
2.or.((llevel.eq.1) .and.(kount.gt.maxmes).and.(mkntrl.eq.1))
3.or.((llevel.eq.2) .and.(kount.gt.max0(1,maxmes)))) go to 100
if (lkntrl.le.0) go to 20
call xerprt(1h ,1)
c introduction
if (llevel.eq.(-1)) call xerprt
1(57hwarning message...this message will only be printed once.,57)
if (llevel.eq.0) call xerprt(13hwarning in...,13)
if (llevel.eq.1) call xerprt
1 (23hrecoverable error in...,23)
if (llevel.eq.2) call xerprt(17hfatal error in...,17)
20 continue
c message
call xerprt(messg,lmessg)
call xgetua(lun,nunit)
do 50 kunit=1,nunit
iunit = lun(kunit)
if (iunit.eq.0) iunit = i1mach(4)
if (ni.ge.1) write (iunit,22) i1
if (ni.ge.2) write (iunit,23) i2
if (nr.ge.1) write (iunit,24) r1
if (nr.ge.2) write (iunit,25) r2
22 format (11x,21hin above message, i1=,i10)
23 format (11x,21hin above message, i2=,i10)
24 format (11x,21hin above message, r1=,e20.10)
25 format (11x,21hin above message, r2=,e20.10)
if (lkntrl.le.0) go to 40
c error number
write (iunit,30) lerr
30 format (15h error number =,i10)
40 continue
50 continue
c trace-back
if (lkntrl.gt.0) call fdump
100 continue
ifatal = 0
if ((llevel.eq.2).or.((llevel.eq.1).and.(mkntrl.eq.2)))
1ifatal = 1
c quit here if message is not fatal
if (ifatal.le.0) return
if ((lkntrl.le.0).or.(kount.gt.max0(1,maxmes))) go to 120
c print reason for abort
if (llevel.eq.1) call xerprt
1 (35hjob abort due to unrecovered error.,35)
if (llevel.eq.2) call xerprt
1 (29hjob abort due to fatal error.,29)
c print error summary
call xersav(1h ,-1,0,0,kdummy)
120 continue
c abort
if ((llevel.eq.2).and.(kount.gt.max0(1,maxmes))) lmessg = 0
call xerabt(messg,lmessg)
return
end
This is the main program:
program dquad
c driver for quadpack
implicit double precision (a-h, o-z)
external f
parameter (limit=50, lenw=4*limit)
dimension iwork(limit), work(lenw)
open (unit=8, file='outquad', iostat=ios, err=100)
c lower limit of itegration
a=-1.0d+00
c upper limit of integration
b=2.0d+00
c absolute accuracy requested
epsabs=1.0d-15
c relative accuracy requested
epsrel=1.0d-09
do 20 l=1, 3000
c key for choice of local integration rule:
c a Gauss-Kronrod pair is used with
c 7 - 15 points if key .lt. 2,
c 10 - 21 points if key = 2,
c 15 - 31 points if key = 3,
c 20 - 41 points if key = 4,
c 25 - 51 points if key = 5,
c 30 - 61 points if key .gt. 5
do 10 key=1, 6
call dqag (f, a, b, epsabs, epsrel, key, result, abserr, neval,
* ier, limit, lenw, last, iwork, work)
if (l .eq. 1) then
write (8, *) 'integral =', result
write (8, *) 'absolute error =', abserr
write (8, *) 'number of integrand evaluations=', neval
end if
10 continue
20 continue
stop
100 write (8, *) 'ios=', ios
stop
end
Also from netlib quad.f.
And this is the function that should be integrated, it's just an example, it not the function that I am gonna integrate in my research,
I just want to learn to integrate quadpack in my code.
double precision function f(x)
implicit double precision (a-h, o-z)
f = 1.0d+00 + x * x + 1.0d+00 / (1.0d+00 + 1.0d+02 * x * x)
return
end
This is from netlib [testf4.f] (http://netlib.org/alliant/quad/testf4.f).
This is xerctl.f
SUBROUTINE XERCTL(MESSG1,NMESSG,NERR,LEVEL,KONTRL)
C***BEGIN PROLOGUE XERCTL
C***DATE WRITTEN 790801 (YYMMDD)
C***REVISION DATE 820801 (YYMMDD)
C***CATEGORY NO. R3C
C***KEYWORDS ERROR,XERROR PACKAGE
C***AUTHOR JONES, R. E., (SNLA)
C***PURPOSE Allows user control over handling of individual errors.
C***DESCRIPTION
C Abstract
C Allows user control over handling of individual errors.
C Just after each message is recorded, but before it is
C processed any further (i.e., before it is printed or
C a decision to abort is made), a call is made to XERCTL.
C If the user has provided his own version of XERCTL, he
C can then override the value of KONTROL used in processing
C this message by redefining its value.
C KONTRL may be set to any value from -2 to 2.
C The meanings for KONTRL are the same as in XSETF, except
C that the value of KONTRL changes only for this message.
C If KONTRL is set to a value outside the range from -2 to 2,
C it will be moved back into that range.
C
C Description of Parameters
C
C --Input--
C MESSG1 - the first word (only) of the error message.
C NMESSG - same as in the call to XERROR or XERRWV.
C NERR - same as in the call to XERROR or XERRWV.
C LEVEL - same as in the call to XERROR or XERRWV.
C KONTRL - the current value of the control flag as set
C by a call to XSETF.
C
C --Output--
C KONTRL - the new value of KONTRL. If KONTRL is not
C defined, it will remain at its original value.
C This changed value of control affects only
C the current occurrence of the current message.
C***REFERENCES JONES R.E., KAHANER D.K., "XERROR, THE SLATEC ERROR-
C HANDLING PACKAGE", SAND82-0800, SANDIA LABORATORIES,
C 1982.
C***ROUTINES CALLED (NONE)
C***END PROLOGUE XERCTL
CHARACTER*20 MESSG1
C***FIRST EXECUTABLE STATEMENT XERCTL
RETURN
END
This is xerror.f
subroutine xerror(messg,nmessg,nerr,level)
c
c abstract
c xerror processes a diagnostic message, in a manner
c determined by the value of level and the current value
c of the library error control flag, kontrl.
c (see subroutine xsetf for details.)
c
c description of parameters
c --input--
c messg - the hollerith message to be processed, containing
c no more than 72 characters.
c nmessg- the actual number of characters in messg.
c nerr - the error number associated with this message.
c nerr must not be zero.
c level - error category.
c =2 means this is an unconditionally fatal error.
c =1 means this is a recoverable error. (i.e., it is
c non-fatal if xsetf has been appropriately called.)
c =0 means this is a warning message only.
c =-1 means this is a warning message which is to be
c printed at most once, regardless of how many
c times this call is executed.
c
c examples
c call xerror(23hsmooth -- num was zero.,23,1,2)
c call xerror(43hinteg -- less than full accuracy achieved.,
c 43,2,1)
c call xerror(65hrooter -- actual zero of f found before interval
c 1 fully collapsed.,65,3,0)
c call xerror(39hexp -- underflows being set to zero.,39,1,-1)
c
c written by ron jones, with slatec common math library subcommittee
c latest revision --- 7 feb 1979
c
dimension messg(nmessg)
call xerrwv(messg,nmessg,nerr,level,0,0,0,0,0.,0.)
return
end
I think the problem is with the messages like this from here:
if(ier.ne.0) call xerror(26habnormal return from dqag ,26,ier,lvl)
but I don't know to fix it.
Can I use in main Fortran 90, where I call the subroutine to integrate? How it is supposed to look? I'm not familiar with fortran 77.
Any suggestion for dealing with this is greatly appreciated.
According to the man pages,
Progress labels are used to define correctness claims. A progress label states the requirement that the labeled global state must be visited infinitely often in any infinite system execution. Any violation of this requirement can be reported by the verifier as a non-progress cycle.
and
Spin has a special mode to prove absence of non-progress cycles. It does so with the predefined LTL formula:
(<>[] np_)
which formalizes non-progress as a standard Buchi acceptance property.
But let's take a look at the very primitive promela specification
bool initialised = 0;
init{
progress:
initialised++;
assert(initialised == 1);
}
In my understanding, the assert should hold but verification fail because initialised++ is executed exactly once whereas the progress label claims it should be possible to execute it arbitrarily often.
However, even with the above LTL formula, this verifies just fine in ispin (see below).
How do I correctly test whether a statement can be executed arbitrarily often (e.g. for a locking scheme)?
(Spin Version 6.4.7 -- 19 August 2017)
+ Partial Order Reduction
Full statespace search for:
never claim + (:np_:)
assertion violations + (if within scope of claim)
non-progress cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 28 byte, depth reached 7, errors: 0
6 states, stored (8 visited)
3 states, matched
11 transitions (= visited+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.293 actual memory usage for states
64.000 memory used for hash table (-w24)
0.343 memory used for DFS stack (-m10000)
64.539 total actual memory usage
unreached in init
(0 of 3 states)
pan: elapsed time 0.001 seconds
No errors found -- did you verify all claims?
UPDATE
Still not sure how to use this ...
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
And let's select testing for non-progress cycles. Then ispin runs this as
spin -a test.pml
gcc -DMEMLIM=1024 -O2 -DXUSAFE -DNP -DNOCLAIM -w -o pan pan.c
./pan -m10000 -l
Which verifies without error.
So let's instead try this with ltl properties ...
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
ltl progress_reader1{ []<> reader[1]#progress_reader }
ltl progress_reader2{ []<> reader[2]#progress_reader }
ltl progress_writer1{ []<> writer[3]#progress_writer }
ltl progress_writer2{ []<> writer[4]#progress_writer }
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
Now, first of all,
the model contains 4 never claims: progress_writer2, progress_writer1, progress_reader2, progress_reader1
only one claim is used in a verification run
choose which one with ./pan -a -N name (defaults to -N progress_reader1)
or use e.g.: spin -search -ltl progress_reader1 test.pml
Fine, I don't care, I just want this to finally run, so let's just keep progress_writer1 and worry about how to stitch it all together later:
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
/*ltl progress_reader1{ []<> reader[1]#progress_reader }*/
/*ltl progress_reader2{ []<> reader[2]#progress_reader }*/
ltl progress_writer1{ []<> writer[3]#progress_writer }
/*ltl progress_writer2{ []<> writer[4]#progress_writer }*/
bool initialised = 0;
init{
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
progress_reader:
assert(true);
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
(initialised == 0)
progress_writer:
assert(true);
od
}
ispin runs this with
spin -a test.pml
ltl progress_writer1: [] (<> ((writer[3]#progress_writer)))
gcc -DMEMLIM=1024 -O2 -DXUSAFE -DSAFETY -DNOCLAIM -w -o pan pan.c
./pan -m10000
Which does not yield an error, but instead reports
unreached in claim progress_writer1
_spin_nvr.tmp:3, state 5, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:3, state 5, "(1)"
_spin_nvr.tmp:8, state 10, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:10, state 13, "-end-"
(3 of 13 states)
Yeah? Splendid! I have absolutely no idea what to do about this.
How do I get this to run?
The problem with your code example is that it does not have any infinite system execution.
Progress labels are used to define correctness claims. A progress
label states the requirement that the labeled global state must be
visited infinitely often in any infinite system execution. Any
violation of this requirement can be reported by the verifier as a
non-progress cycle.
Try this example instead:
short val = 0;
init {
do
:: val == 0 ->
val = 1;
// ...
val = 0;
:: else ->
progress:
// super-important progress state
printf("progress-state\n");
assert(val != 0);
od;
};
A normal check does not find any error:
~$ spin -search test.pml
(Spin Version 6.4.3 -- 16 December 2014)
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
cycle checks - (disabled by -DSAFETY)
invalid end states +
State-vector 12 byte, depth reached 2, errors: 0
3 states, stored
1 states, matched
4 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.292 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
unreached in init
test.pml:12, state 5, "printf('progress-state\n')"
test.pml:13, state 6, "assert((val!=0))"
test.pml:15, state 10, "-end-"
(3 of 10 states)
pan: elapsed time 0 seconds
whereas, checking for progress yields the error:
~$ spin -search -l test.pml
pan:1: non-progress cycle (at depth 2)
pan: wrote test.pml.trail
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Partial Order Reduction
Full statespace search for:
never claim + (:np_:)
assertion violations + (if within scope of claim)
non-progress cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 20 byte, depth reached 7, errors: 1
4 states, stored
0 states, matched
4 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.292 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
pan: elapsed time 0 seconds
WARNING: ensure to write -l after option -search, otherwise it is not handed over to the verifier.
You ask:
How do I correctly test whether a statement can be executed arbitrarily often (e.g. for a locking scheme)?
Simply write a liveness property:
ltl prop { [] <> proc[0]#label };
This checks that process with name proc and pid 0 executes infinitely often the statement corresponding to label.
Since your edit substantially changes the question, I write a new answer to avoid confusion. This answer addresses only the new content. Next time, consider creating a new, separate, question instead.
This is one of those cases in which paying attention to the unreached in ... warning message is really important, because it affects the outcome of the verification process.
The warning message:
unreached in claim progress_writer1
_spin_nvr.tmp:3, state 5, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:3, state 5, "(1)"
_spin_nvr.tmp:8, state 10, "(!((writer[3]._p==progress_writer)))"
_spin_nvr.tmp:10, state 13, "-end-"
(3 of 13 states)
relates to the content of file _spin_nvr.tmp that is created during the compilation process:
...
never progress_writer1 { /* !([] (<> ((writer[3]#progress_writer)))) */
T0_init:
do
:: (! (((writer[3]#progress_writer)))) -> goto accept_S4 // state 5
:: (1) -> goto T0_init
od;
accept_S4:
do
:: (! (((writer[3]#progress_writer)))) -> goto accept_S4 // state 10
od;
} // state 13 '-end-'
...
Roughly speaking, you can view this as the specification of a Buchi Automaton which accepts executions of your writer process with _pid equal to 3 in which it does not reach the statement with label progress_writer infinitely often, i.e. it does so only a finite number of times.
To understand this you should know that, to verify an ltl property φ, spin builds an automaton containing all paths in the original Promela model that do not satisfy φ. This is done by computing the synchronous product of the automaton modeling the original system with the automaton representing the negation of the property φ you want to verify. In your example, the negation of φ is given by the excerpt of code above taken from _spin_nvr.tmp and labeled with never progress_writer1. Then, Spin checks if there is any possible execution of this automaton:
if there is, then property φ is violated and such execution trace is a witness (aka counter-example) of your property
otherwise, property φ is verified.
The warning tells you that in the resulting synchronous product none of those states is ever reachable. Why is this the case?
Consider this:
active [2] proctype writer()
{
1: assert(_pid >= 1);
2: (initialised == 1)
3: do
4: :: else ->
5: (initialised == 0);
6: progress_writer:
7: assert(true);
8: od
}
At line 2:, you check that initialised == 1. This statement forces writer to block at line 2: until when initialised is set to 1. Luckily, this is done by the init process.
At line 5:, you check that initialised == 0. This statement forces writer to block at line 5: until when initialised is set to 0. However, no process ever sets initialised to 0 anywhere in the code. Therefore, the line of code labeled with progress_writer: is effectively unreachable.
See the documentation:
(1) /* always executable */
(0) /* never executable */
skip /* always executable, same as (1) */
true /* always executable, same as skip */
false /* always blocks, same as (0) */
a == b /* executable only when a equals b */
A condition statement can only be executed (passed) if it holds. [...]